Marko Novakovic
01/27/2023, 9:42 AM!
will come back in place of .bind()
? I really liked it 😄Stylianos Gakis
01/27/2023, 9:43 AMMarko Novakovic
01/27/2023, 9:46 AMfun a() = Either.Right(1)
fun b() = Either.Right(2)
fun sum() = either {
!a() + !b()
}
something like thatCLOVIS
01/27/2023, 9:49 AM.bind
, but I really dislike !
, it's really not readable to me. One option would be if the Arrow Meta team created their own compiler plugin which added an operator just for that, but…
…with Arrow 2.0 we will almost never use .bind
, so why bother?
context(Raise<Error>)
fun a() = 1
context(Raise<Error>)
fun b() = 2
context(Raise<Error>)
fun sum() {
a() + b()
}
simon.vergauwen
01/27/2023, 9:49 AMcontext(EffectScope<E>)
suspend operator fun <E, A> Either<E, A>.not(): A = bind()
context
receivers you can indeed get rid of bind
completely as well 😄
But that'll heavily depend on what style you prefer, I expect many people to still use Either
.Stylianos Gakis
01/27/2023, 9:50 AMMarko Novakovic
01/27/2023, 9:51 AMsimon.vergauwen
01/27/2023, 9:51 AM…with Arrow 2.0 we will almost never useI'd argue this is more context receivers than 2.0, you can already do the same today in 1.x.x with context receivers., so why bother?.bind
Marko Novakovic
01/27/2023, 9:51 AMCLOVIS
01/27/2023, 9:52 AMBut that'll heavily depend on what style you prefer, I expect many people to still useWe'll see. Personally this is the syntax that was missing for me to really embrace "Effect is a function, Either is its result after execution" concept.Either
simon.vergauwen
01/27/2023, 9:52 AMCLOVIS
01/27/2023, 9:53 AMEither
as a return type everywhere and having to always use bind
, feels 'un-kotlin-y', like using await
after each function callraulraja
01/27/2023, 9:55 AMcontext(Deps) Effect<E, A>.run(): E | A
where you can raise(e)
or return a
and then there is not even an Either. https://github.com/47deg/TBD/blob/main/scala-fx/src/main/scala/fx/Runtime.scala#L13simon.vergauwen
01/27/2023, 9:57 AMsuspend
, and the syntax for me is the killer feature and also why Loom is IMO non-impactful for Kotlin since the syntax is the killer feature. Similarly with ?
actually, it is in a very similar spirit as well.CLOVIS
01/27/2023, 9:59 AMsuspend
Marko Novakovic
01/27/2023, 10:01 AMCLOVIS
01/27/2023, 10:02 AMsimon.vergauwen
01/27/2023, 10:04 AMMarko Novakovic
01/27/2023, 10:06 AMbut they were all fix-able by re-writing them slightly differentlyI have pretty basic example, one from above,
context(Raise<Error>)
fun a() = 1
but getting errorsimon.vergauwen
01/27/2023, 10:06 AMMarko Novakovic
01/27/2023, 10:07 AMThe feature "context receivers" is experimental and should be enabled explicitly
but I have
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().all {
kotlinOptions {
jvmTarget = "11"
freeCompilerArgs += listOf("-Xcontext-receivers", "-opt-in=kotlin.RequiresOptIn")
}
}
inside build.gradle.kts
, shared KMM modulesimon.vergauwen
01/27/2023, 10:08 AMCLOVIS
01/27/2023, 10:08 AMMarko Novakovic
01/27/2023, 10:08 AMCLOVIS
01/27/2023, 10:08 AMMarko Novakovic
01/27/2023, 10:09 AMCLOVIS
01/27/2023, 10:09 AMphldavies
01/27/2023, 10:20 AMOlaf Gottschalk
02/20/2023, 8:11 PMsimon.vergauwen
02/20/2023, 8:14 PMRaise<E>
and Either
are equivalent and isomorphic, but different notation. In theory you can also compare Either
with checked exceptions but Raise
looks more familiar in syntax.IO
, Either
, etc since “capabilities” can be composed but monads cannot.phldavies
02/20/2023, 8:25 PMRaise<E>
effectively provides a handler, which can be as lightweight or heavy as required, for errors directly to the function being called (and thus must be available) whereas checked exception mechanism is forced throughout the entire stack for propagation.Olaf Gottschalk
02/20/2023, 8:25 PMphldavies
02/20/2023, 8:30 PMRaise<E>
and isn't forcibly conflated with truly exceptional flows.simon.vergauwen
02/20/2023, 8:36 PMBut that was maybe more due to lazy programmers just bubbling them up instead of transforming them on the way up...That is kind-of a design decision I guess 😁 They definitely got abused a lot in Java, but not really sure what the root of that was. I think it was a combination of a lot of bad practices. Perhaps it’s also a better thing that in Kotlin these concepts live in libraries and not in the language / Std. Similarly to how KotlinX Coroutines is a library, and not like Future in Scala.
Stylianos Gakis
02/20/2023, 11:02 PMRaise<E>
for example?
As a consumer of some library, would you prefer this which would then also need to expose arrow's api, or thrown exceptions? Or their own result type which wouldn't force any dependency. Or if none of the above, what else?Marko Novakovic
02/21/2023, 11:03 AMsimon.vergauwen
02/21/2023, 11:10 AMEither
type or Raise
if context receivers are available. I.e. https://github.com/nefilim/kjwt.
A custom ADT could also work, if you're not relying on Arrow for the internals of your library and in that case an arrow-integration module should be quite easy to offer. With context receivers you could even offer Arrow's bind
syntax for your custom ADTs.Stylianos Gakis
02/21/2023, 11:20 AMEither
, you’d have to have that dep as api
and expose all of it. Which comes with the problem that if your lib has a different arrow dependency, then you are exposing that and so on.
But yeah, I see what you mean, and I think the idea of keeping it as original as possible for use cases like JDBC makes a ton of sense, while it may not if you are building a very specific library which advertises that it exposes some nice APIs using arrow like this kjwt does. Thanks a lot for the response!phldavies
02/21/2023, 11:22 AMRaise<E>
without leaking Either
or Raise
simon.vergauwen
02/21/2023, 12:31 PMWhich comes with the problem that if your lib has a different arrow dependency, then you are exposing that and so on.Which is why Arrow makes a big point of being binary compatible, and following semantic versioning. This should never be an issue within major versions. In the end the Kotlin Std, KotlinX Coroutines, RxJava/Project Reactor, Guava, Apache Commons, etc also suffers from this issue, and in the Kotlin Std I've even come across binary incompatibility issues minor version albeit relying on
@ExperimentalTime
so I willingly holding a gun in my hand...
Additionally, a big priority of Arrow 2.x was to make the binary really small. Which is a second benefit of making the API more uniform, and getting rid of dead weight. By doing this the threshold of exposing Arrow should be lower, at least from a binary size point of view in contrast to for example RxJava/Reactor/Guava which have much bigger binaries than Arrow even 1.x.x. This was a big issue for Arrow pre 1.x.x already.It may not be intended, but it seems the compiler will preference resolving a context-receiver method if the context is available over the non-context-receiver method, allowing a default context to be provided in an overload.This is intended by the Kotlin language, and it's actually a great technique for overloading top-level functions within a receiver lambda. The reasoning is that if you're inside of
R.() -> A
then this
is R
and you're thus calling method functions instead of top-level and methods have precedence of top-level. So it's behavior that was explicitly designed like this in Kotlin.phldavies
02/21/2023, 12:34 PM