dnowak
03/14/2023, 1:26 PMIO
was in early versions of arrow?
2. Any advantage of using Effect
compared to suspended function
?
3. Where can I find some “reference” code showing how to use effects - combining small effects into larger ones, using effects in the application?simon.vergauwen
03/14/2023, 1:36 PMalpha
to be released as 1.2.0-RC
Effect
is a typealias
. typealias Effect<E, A> = suspend Raise<E>.() -> A
. So you can say that suspend == IO
and suspend Raise<E>.() -> A
is EitherT<IO
or a polymorphic version of suspend fun f(): Either<E, A>
where Raise<E>
can represent any data type (MonadError<F, E>
).
2. They're equal. Effect
is a suspend fun
+ the ability to work over typed errors of E
.
3. We're working on some documentation/website still, but it's the same as working over suspend fun f(): Either<E, A>
. If you were doing suspend fun f(): Either<E, A> = either { }
today you can replace it with fun f(): Effect<E, A> = effect { }
, suspend fun Raise<E>.f(): A
or context(Raise<E>) suspend fun f(): A
and all 3 works seamlessly together and are isomorphic with each-other. Note that val e: Either<E, A>
is a value and val f: Effect<E, A>
is a value of a computation.
For some examples, you can check:
• Ktor Example
• Ktor Example with Context Receivers
• Github Alerts Subscriptions
• Github Alerts Subscriptions with Context Receivers
If you were already familiar with EffectScope<E>
and EagerEffectScope<E>
then Raise<E>
is simply the combination of the two. There is no more need to distinct between suspend
and `non-suspend`(eager). We now leverage inline
from the compiler to simplify this pattern and enable more powerful abstractions/patterns.dnowak
03/14/2023, 5:27 PMEffect<E, A>
and suspended fun f(): Either<E, A>
are equal why the effect was introduced?simon.vergauwen
03/14/2023, 5:42 PMEffect<E, A>
(suspend Raise<E>.() -> A
) is a DSL that enables syntax over Either<E, A>
in a generic/polymorphic way.
Given context receivers lets consider the following:
context(arrow.core.raise.Raise<E>)
fun <E, A> io.vavr.control.Either<E, A>.bind(): A =
fold({ raise(it }, { it })
fun javaSdk(): io.vavr.control.Either<String, Int> =
io.vavr.control.Either.right(1)
fun otherCode(): arrow.core.Either<String, Int> =
arrow.core.Either.Right(1)
fun Raise<String>.one(): Int = 1
val x: arrow.core.Either<String, Int> = either {
javaSdk().bind() + otherCode().bind() + one()
}
fun Raise<String>.x2(): Int =
javaSdk().bind() + otherCode().bind() + one()
val f: Effect<String, Int> = ::x2
simon.vergauwen
03/14/2023, 5:50 PMRaise<E>
is the important piece, Effect<E, A>
is just a typealias
for suspend Raise<E>.() -> A
. The either { }
DSL is powered by Raise<E>
, as well as many other types and offers the flexibility to bridge custom types into the DSL as well.
If you're familiar with Scala, or Haskell, you can consider Raise<E>
final tagless to work over error types. It only defines E
but not the container F
.dnowak
03/15/2023, 2:06 PMsimon.vergauwen
03/15/2023, 2:06 PM