Wesley Hartford
02/29/2024, 12:13 AMjava.lang.IllegalStateException: Returning a lazy computation or closure from 'fold' breaks the context scope, and may lead to leaked exceptions on later execution.
Make sure all calls to 'raise' and 'bind' occur within the lifecycle of nullable { }, either { } or similar builders.
See Arrow documentation on 'Typed errors' for further information.
Is this error new in version 1.2.2? I haven't changed any code other than the arrow upgrade. It seems like it's trying to tell me that I'm doing something like this:
fun bad() = either {
{ calledLazily().bind() }
}
Which is bad because the bind
may be called outside of the either
scope it implicitly refers to. Unfortunately, I cannot see what part of my code is doing anything like that.Wesley Hartford
02/29/2024, 12:14 AMfun main() {
either<String, () -> String> { {"foo bar"} }
}
Running this main function results in the following:
Exception in thread "main" java.lang.IllegalStateException: Returning a lazy computation or closure from 'fold' breaks the context scope, and may lead to leaked exceptions on later execution.
Make sure all calls to 'raise' and 'bind' occur within the lifecycle of nullable { }, either { } or similar builders.
See Arrow documentation on 'Typed errors' for further information.
at com.ze.juicy.grpc.stub.TestKt.main(test.kt:32)
at com.ze.juicy.grpc.stub.TestKt.main(test.kt)
Wesley Hartford
02/29/2024, 12:25 AMAlejandro Serrano.Mena
02/29/2024, 8:16 AMraise
inside, would lead to problemsAlejandro Serrano.Mena
02/29/2024, 8:17 AMbind
you do hereAlejandro Serrano.Mena
02/29/2024, 8:18 AMeither
in that case would return a Right
... but inside the lazy computation/lambda you may raise
and then... what would you expect to happen? since this interaction is always problematic, and lead to hard-to-diagnose problems, we've decided to leave it outsimon.vergauwen
02/29/2024, 8:34 AMRaise
as a context (or extension) that way your lazy computation remains within the scope Raise
.
There is 2 checks that happen:
1. When the result is returned from either
, (actually fold
for Raise
) we check (on best effort) if no lazily value is returned.
2. When you capture a lazy bind
call (so leak Raise
) and try to invoke it out of its scope then it'll throw an IllegalStateException
.
So, what you can do is the following:simon.vergauwen
02/29/2024, 8:36 AMbind
.
data class MyLazyCode(val block: () -> Int)
either<String, MyLazyCode> {
MyLazyCode {
//bind is illegal here
}
} // <-- returns Right(MyLazyCode)
Alejandro Serrano.Mena
02/29/2024, 8:36 AMfoldUnsafe
yourselfsimon.vergauwen
02/29/2024, 8:37 AMfoldUnsafe
is there too. Forgot about that. 👍 You could define a custom eitherUnsafe
as well.Wesley Hartford
02/29/2024, 3:58 PMeither
block: validating some input, and building a function using that input. Only the validation portion made use of the Raise
, so I moved the creation of the resulting function out of the either
block.simon.vergauwen
02/29/2024, 4:30 PMWesley Hartford
02/29/2024, 7:53 PMWesley Hartford
02/29/2024, 9:27 PMFunction
, Lazy
, or Sequence
. None of the instances I've found in my code were actually calling those functions, they were just returning a Function
.Wesley Hartford
02/29/2024, 10:55 PMPredicate
(which is a typealias for () -> Boolean
). All the implementations I'm returning are quite simple and don't interact with the raise context at all. I ended up having to implement three functions following the unsafe pattern you mentioned above: eitherUnsafe
, withErrorUnsafe
, and recoverUnsafe
.