https://kotlinlang.org logo
Title
d

dave08

02/22/2023, 4:22 PM
I'm not too sure I understand what the purpose of it is compared to just getting results from Either...
s

simon.vergauwen

02/22/2023, 4:29 PM
Either
is a value, where
Effect<E, A>
is
suspend Raise<E>.() -> A
.
Either
is the result of invoking
(suspend) Raise<E>.() -> A
.
When you pass me an
Effect<E, A>
I can invoke it many times, but if you pass me a
Either<E, A>
value I can only inspect it but I cannot recompute it. If I need to recompute it then you need to pass me
(suspend) () -> Either<E, A>
or
(suspend) Raise<E>.() -> A
.
Would that be beneficial to include that explanation like this in the updated docs?
d

dave08

02/22/2023, 4:40 PM
I think such an explanation is not concrete enough... the question that comes to my mind now is "when do you need to recompute it vs. inspecting it"...? You're still talking about how things are done, not WHY they are done that way.
s

Sam Painter

02/22/2023, 4:43 PM
For the same reasons you ever want a lazy value. To defer expensive computations. For optimization of the computation. For injecting a different dependency to the computation. For executing something around the computation. There are lots of reasons you may want an Effect.
d

dave08

02/22/2023, 4:43 PM
I noticed Simon used either in the routes of the ktor sample project. I don't remember where I saw this code:
private suspend inline fun <reified A : Any> KtorCtx.respond(
    statusCode: HttpStatusCode,
    noinline action: suspend Raise<ServiceError>.() -> A
): Unit = effect(action).fold(
    { error ->
        when (error) {
            is ServiceError -> call.respond(BadRequest, error)
//            is UserAlreadyExists -> call.respond(Conflict, "${error.user} already exists")
//            is UserNotFound -> call.respond(NotFound, "User with id ${error.id} not found")
        }
    }) { a -> call.respond(statusCode, a) }
Then in the Ktor route the action lambda surrounds the whole thing.
For the same reasons you ever want a lazy value.
So why would it be used in that previous case?
s

simon.vergauwen

02/22/2023, 4:47 PM
In function form they're isomorphic to each-other:
suspend fun one(): Either<String, Int> = TODO("")

fun two(): Effect<String, Int> =
  effect { one().bind() }

suspend fun three(): Either<String, Int> =
  either { two().bind() }
d

dave08

02/22/2023, 4:47 PM
And how is that lazier than either...?
s

simon.vergauwen

02/22/2023, 4:49 PM
suspend fun
is what makes
one
and
three
"lazy". Here they they have very different meaning however.
val one: Either<String, Int> = 1.right()
val two: Effect<String, Int> = effect { 1 }
You can inspect
one
but you cannot do anything with
two
unless you invoke it in some
suspend fun
first.
The power of
Raise
is that it power everything in Arrow, it allows this entire mechanism of
either { }
,
ensure
,
ensureNotNull
etc to work. All these APIs and DSLs are build on-top of it.
Effect
is also not even a type anymore, it's just a
typealias
for a lambda.
d

dave08

02/22/2023, 4:53 PM
That's a bit more clear, but the naming is a bit confusing then... so it's really a lazyEither?
Or a deferredEither (aka coroutines deferred)
s

simon.vergauwen

02/22/2023, 4:55 PM
I would actually say it's the other way around,
Either
is a computedEffect 😅 but it depends a bit on whether it was an
Effect
or
EagerEffect
since
suspend
is allowed to pass through the runtime
d

dave08

02/22/2023, 4:56 PM
That's more a terminology that Kotliners recognize computedEffect and co. is more for the mathematicians... 😃
Deferred is a suspend fun that needs to be run
s

simon.vergauwen

02/22/2023, 4:57 PM
That's not what Deferred means in KotlinX 🙃
d

dave08

02/22/2023, 4:58 PM
You're right... it depends on the parameters...
It runs right away and you await the result in general...
But some use it as a way to create a suspend function that can be run somewhere where there is a suspend... https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/
I guess naming is HARD... 🤒
One way or the other it was confusing to have EffectScope deprecated on one hand, and on the other still have effect { } around (which might even mean something a bit different...) I still don't understand why it was used in the code above. (And I don't remember from which sample of Simon's I took it... 🙈...)
But things are a bit clearer now... thanks!
s

simon.vergauwen

02/22/2023, 5:04 PM