I’ve just finished reading through Grokking Functi...
# arrow
s
I’ve just finished reading through Grokking Functional Programming, which has helped me to finally grasp the mental model of functional IO/side-effects. As I’ve been reading I’ve been trying to map the various concepts from Scala over to Arrow. I’ve read the docs on Arrow’s decision to use suspending functions as a replacement for an IO monad. I’m curious though about the role of some of the other remaining constructs in Arrow Core, namely
Effect
and
Eval
. At first glance I see that Eval isn’t really an extension of
suspend () -> A
but instead seems to have the purpose of collapsing potentially large callstack operations. Effect seems to provide functions like
attempt
and
fold
but seems a bit more like a wrapper around
suspend () -> A
. Is it there to provide an extra layer of “semantics”, such as making it clear that a function returns a side-effecting operation versus just a suspending one? Should
Effect
be used as a return type over
suspend () -> A
? or are there use-cases for both?
👀 1
s
Hey Sam,
Effect
is not really the interesting part, but rather
EffectScope
or it's new named alternative
Raise
. Which allow you to bring the powers of typed errors, which is kind-of unrelated to
IO
. You can see
Effect<E, A>
as
EitherT[IO, E, A]
, but without transformers or nesting of monads.
You can achieve the same powers using
suspend fun example(): Either<E, A>
where
either { }
DSL works through
EffectScope<E>
.
Effect
will be a simple
typealias
to
suspend Raise<E>.() -> A
in 2.x.x and 1.2.x.
I'm not sure how
Eval
is discussed in Grokking Functional Programming, but it's quite useless in Kotlin and will probably be marked for deprecation towards 2.x.x
Afaik in Scala it's also unrelated to
IO
s
Eval wasn't discussed in the book, but as I was looking through Arrow's API it almost seemed like Eval and Effect semantically represented two types of side-effects, Eval for producing a value lazily and Effect for performing some side-effecting action.
So with Effect is it generally a good principle to return Effect from functions instead of returning
suspend () -> A
or
suspend () -> Either<E, A>
? It's functionally equivalent but provides some additional conveniences in terms of the EffectScope DSL and other functions correct?
as well as being semantically clearer to the user that it's something that will carry out some side effect
and capturing the error handling concern in the type as well without the need to use an Either explicitly
s
I wouldn't return
suspend () -> A
from a function, but rather use
suspend fun name(): A
. You rarely have to work with a
suspend () -> A
value. You can think of it as marking a function with the
io
capabilities, and allowing you to use
invoke
instead of
flatMap
for composing them. Similarly you can then simply use
Either
in the return type whenever needed.
s
yea, it seems like that's also a bit of a mental shift from the IO Monad style
s
Eval
is used for stack-safety on the JVM, but since recently Kotlin offers other solutions for that. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-deep-recursive-function/ But you can use
tailrec
in combination with
suspend fun
.
It's a much simpler model then
IO
monad, but you get used to it very quickly. It maps 1-on-1 to each other, but it just looks a bit different 😄
j
Hi, so is it ok to say that Effect retuns a description of a program that might throw exceptions when executed?
s
Yes,
Effect<E, A>
describes a function that results in an typed error
E
, and exception
Throwable
or a value
A
. In 1.2.x backport for 2.x.x it's very clear in the type, since it will now just be
typealias Effect<E, A> = suspend Raise<E>.() -> A
.
j
i was writting repositories and adapters to external services using either {} instead of effect {} , i guess that was not the correct way as it is evaluated as soon as i execute it in my free side effect use cases 😕
s
Hmm, using
Either
is not incorrect as long as you write
suspend fun program(): Either<E, A>
if you want to store it as
val
or regular
fun
then you'll want
Effect<E, A>
.
Raise<E>
is the interesting bit though, as you can see in the
typealias
. Instead of using
effect { }
you'll also be able to to write
suspend fun Raise<E>.program(): A
or with context receivers
context(Raise<E>) suspend fun program(): A
. I am working on new documentation for this. All of this is available in
1.1.6-alpha.27
or in the
1.2.0
we'll release in next weeks.
I'm also working on documentation to explain more clearly the differences, and similarities between the two types. Here is my StackOverflow answer, but be sure to ask any other questions you have! I'm happy to help, and it'll give me a better idea on how to write good docs 😉 https://stackoverflow.com/questions/72277426/which-should-we-choose-between-effect-and-either-as-a-return-type-of-our-busines/72277572#72277572
j
This is something i am working on right now. In order to deal with side effect functions i have created a free side effect function which returns a description of a “possible side effect function”.This way i can put this function in my pure functional core. I was able to achieve this with Effect but not with Either. That is why i have said “incorrect” (i was missing some context, sorry)
Copy code
class FreeSideEffectRepositoryImpl : FreeSideEffectRepository {

    override suspend fun storeEffect(reservation: Reservation): Effect<Unit, Unit> = effect {
        //this is executed when evaluating to* methods
        //eg.: FreeSideEffectRepositoryImpl.storeEffect(Reservation()).toEither()
        throw IllegalArgumentException("error in effect")
    }

    override suspend fun storeEither(reservation: Reservation): Either<Unit, Unit> = either {
        //this is executed as soon as FreeSideEffectRepositoryImpl.storeEither(Reservation()) is executed
        throw IllegalArgumentException("error in either")
    }
}
s
Right, you're correct here. It's a bit tricky to clearly define here depending on from which background you might come.. PS: you can safely remove
suspend
from
suspend fun storeEffect(reservation: Reservation): Effect<Unit, Unit>
since
effect { }
already gives you the power of
suspend
lazily inside.
j
I have created a small gist with an example of functional core/imperative shell approach just to validate the idea with arrow. If you could have it a quick look it would be great, thanks. https://gist.github.com/jorgebo10/10567d0f7979fea1faabe2c6aef291dd