Jordi Pradel
12/07/2022, 5:35 PMcontext(Raize<...>)
. e.g.:
context(Raise<AccountNotFound>)
suspend fun getNumberOfOperationsIn(accout: AccountNumber): Natural
I understand Either
will still be idiomatic for pure functions that may return an error (that is programs whose only side effect can be to fail with a domain error). I'm thinking of smart constructors for my domain classes (e.g. a function that takes an Int
and returns either a NaturalNumber
or a IllegalNaturalNumber
error). I wouldn't like to make such a constructor a suspend fun
because it is otherwise a pure function.
I have some questions regarding this new approach (to apply whenever Arrow 2 and Kotlin's context receivers are production ready):
1. Is the usage of Either
I described the expected?
2. Is programming such pure functions that can fail the purpose of the eagerEffect
function?
3. Does the `effect`function still have idiomatic usages?simon.vergauwen
12/07/2022, 6:07 PMcontext(Raise<E>) () -> A
is a function that when computes naturally results in Either<E, A>
if you consider fold({ it.left() }, { it.right() })
. In that sense you might say that a fun f(): Either<E, A>
is isomorphic to context(Raise<E>) () -> A
since f
is a function so it only computes the Either
when invoked. So you can use whatever fits your purpose best.
2. Important to note that in 2.x.x Raise
will no longer imply suspend
, in contrast to EffectScope
. So currently you have eagerEffect
& effect
which are data types, but in 2.x.x they're a simply typealias
for respectively Raise<E>.() -> A
and suspend Raise<E>.() -> A
.
3. effect
(might be renamed to `raise`to match the name of the DSL) will become a simple function that leverage @BuilderInference
to automatically infer the E
type for suspend Raise<E>.() -> A
. So it's a helper function more than anything else, if you have context(Raise<E>)
there is no need for inference. If you meant Effect/EagerEffect
as returns types, I don't think so. We hope that context(Raise<E>)
will become mainstream and otherwise Either
is probably more desired.
Couple of additional notes, with context receivers you will be able to annotate a constructor
with Raise<E>
such that you annotate your regular constructor with Raise<IllegalNaturalNumber>
which doesn't even require you to make smart-constructors unless you require suspend
.simon.vergauwen
12/07/2022, 6:10 PM1.1.4
and in the next weeks I hope to back-port Raise
to 1.2.0
so that we have a smooth migration period to 2.x.x
in the coming 4~5 months.
I'll provide migrations guides, and migration scripts where applicable. So in practice you should be able to leverage the new powers I mentioned above from January.
Not having to distinct between EagerEffect
and Effect
(or EagerEffectScope
and EffectScope
) will be great. Also either
vs either.eager
will not longer be needed, and it'll be all inlined.
Making either { transform(fa.bind()) }
as cheap as fa.map(transform)
.simon.vergauwen
12/07/2022, 6:11 PMI'm thinking of smart constructors for my domain classes (e.g. a function that takes anIn the meantime you can just useand returns either aInt
or aNaturalNumber
error). I wouldn't like to make such a constructor aIllegalNaturalNumber
because it is otherwise a pure function.suspend fun
EagerEffect/EagerEffectScope
this leverages @RestrictSuspension
to disallow foreign suspend fun
to be used inside the DSL making it pure.simon.vergauwen
12/07/2022, 6:12 PMA Scala (+ Cats + Cats effect) functional programmerIf you have any questions, or feedback I'd love to hear it! I hope you enjoy working with Kotlin and Arrow ☺️
Jordi Pradel
12/09/2022, 9:42 PMJordi Pradel
12/09/2022, 9:44 PM1.... . In that sense you might say that aAgree. I'm aware of that and I'm trying to understand what will be the idiomatic way in the future. I think being able to define a function that returns whateveris isomorphic tofun f(): Either<E, A>
...context(Raise<E>) () -> A
A
you want for the success scenario is wonderful. So I'd prefer context(Raise<E>) () -> A
.Jordi Pradel
12/09/2022, 9:58 PM2. Important to note that in 2.x.xOh! That's something I was missing! But I'm not sure I understand correctly. Will you be able to define a non suspend function withwill no longer implyRaise
, ...suspend
context(Raise<E>)
and then use shift
, ensure
& co. from that function? Currently I can use context(EagerEffectScope)
but ensure
is still a suspend fun
, so I need to define my function as suspend
too. Or I can use eagerEffect{...}
but then I get the EagerEffect
datatype you mentioned instead of a return type of A
.Jordi Pradel
12/09/2022, 10:03 PM3.Ok! I wasn't sure `Effect`/`EagerEffect` as return types would not be idiomatic in the future. I understand. When you say... it's a helper function more than anything else,...effect
effect
and eagerEffect
functions will be just helper functions... that means I will be able to program without them? Or I'll need eagerEffect
to be able to use shift
on non suspend functions?Jordi Pradel
12/09/2022, 10:06 PMCouple of additional notes, with context receivers you will be able to annotate aWow! Nice!withconstructor
Raise<E>...
Jordi Pradel
12/09/2022, 10:11 PMI'll provide migrations guides, and migration scripts where applicable.Thanks for your hard job! If you accept the suggestion, I think it is important to document this kind of stuff very clearly for newcomers too. I think this will be a great opportunity to make Arrow aproachable for teams that didn't dare to in the past because of its complexity (and the dreaded monads). Of course, asking you (Simon and all the team) more than you already do is asking too much, so let me know if I can help (despite my non native English).
Jordi Pradel
12/09/2022, 10:26 PMflatMap
everywhere is already quite daunting (even in Scala, where we have for comprehensions). Using those HKTs in Kotlin was not something I feel like convincing any team of.Jordi Pradel
12/09/2022, 10:30 PMIO
(or F{_]
...).
I read KEEP-87 in awe back in 2017... and now, in just a few months, I'll have much less difficulty "selling" Arrow (and FP) to the teams I work with. That's simply wonderful.Jordi Pradel
12/09/2022, 10:46 PMJordi Pradel
12/09/2022, 11:15 PMsimon.vergauwen
12/10/2022, 10:06 AMWill you be able to define a non suspend function withYes, this will all be possible withoutand then usecontext(Raise<E>)
,shift
& co. from that function?ensure
suspend
☺️
If you accept the suggestion, I think it is important to document this kind of stuff very clearly for newcomers too.After having worked very hard on this new encodings, and a migration plan it's finally time to put actual time on this. So when I mentioned I'll be backporting things to 1.x.x it's mostly to provide a as pain free as possible migration towards 2.x.x. Things includes re-working a lot of the documentation. Getting help on it will be great, and I'll be regularly sharing PRs here and in #arrow-contributors for people to proof-read and give feedback on the documentation. So if you're interested in helping keep an eye out for it, we're also hoping to add more issues on Github in the near future, that are easy for people to work on.
For many teams I've worked with, usingThis was exactly our experience as well, and is what set us on this journey to look for better solutions. Kotlin has exactly what we need to do so.everywhere is already quite daunting (even in Scala, where we have for comprehensions).flatMap
When you sayYes, they leverageandeffect
functions will be just helper functions... that means I will be able to program without them?eagerEffect
@BuilderInference
to infer the E
type, and the A
type is then inferred from the return type of the lambda. So you can do.
fun example(): Either<String, Int> = 1.right()
val eff = effect { example().bind() }
Kotlin is able to infer both E = String
and A = Int
here thanks to @BuilderInference
.simon.vergauwen
12/10/2022, 10:09 AMJordi Pradel
12/10/2022, 10:15 AMsimon.vergauwen
12/10/2022, 10:16 AM