I want to init a class `val` property in the `init...
# arrow
l
I want to init a class
val
property in the
init
block of a class based on some
Either
value. However, whenever I use any of the
Either
related functions like
fold
or
either.eager
block (which take a lambda as parameter) and I try to set the class property inside the lambda I get the error "Captured member variables initialization is forbidden due to possible reassignment". Is my only option really something like:
Copy code
myProperty = if (either.isRight()) either.value else defaultValue
?
s
Copy code
myProperty = either.fold({ defaultValue }, { it })
This doesn't work?
l
Hmm, yes, that would work. Thanks!
s
What about
.orNull() ?: defaultValue
or
getOrElse
?
l
For some reason I didn't think about pulling
myProperty
out of the lambda...
s
Ah I see. Yes, that is not allowed by the Kotlin compiler... a bit strange though since with
inline fun
it should be perfectly valid.
l
I was doing
either.fold({ myProperty = defaultValue }, { myProperty = it })
But your syntax is more concise anyway.
s
any reason you are not doing it the other way around? e.g.
etiher.fold({}, {})
and creating your class inside the lambda... that way your class doesn't have to know about
Either
l
No, actually the
init
block is inside the class
The background is, the class being initialized is a viewmodel. It depends on a repository which returns an
Either
. I want to initialize the UI state of the viewmodel based on the value returned by the repository.
y
@simon.vergauwen the reason it's not allowed by the compiler is the aforementioned functions are missing a callsInPlace contract with
AT_MOST_ONCE
or
EXACTLY_ONCE
s
Aha, then we should definitely add those! Thanks for the tip @Youssef Shoaib [MOD]. I'm going to create a ticket for this.
@Youssef Shoaib [MOD] this is not valid for
fold
since we need to specify
AT_MOST_ONCE
for both lambdas but the compiler now still won't now which one of the two lambdas will be executed, and that both will not be executed.
Copy code
inline fun <E, A, B> Either<E, A>.fold(
  error: (E) -> B,
  transform: (A) -> B
): B {
  contract {
    callsInPlace(error, InvocationKind.AT_MOST_ONCE)
    callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
  }
So it will complain that
either.fold2({ myProperty = "defaultValue" }, { myProperty = it })
the second assignment is invalid since
val
can only be assigned once.
Created this ticket for further investigations. https://github.com/arrow-kt/arrow/issues/2806
@Lukasz Kalnik regardless if whether this can be fixed with Kotlin Contracts I think the nicest pattern is
fold/transform
the value and then assign in a single statement like the fix that we discussed above.
myProperty = either.fold({ defaultValue }, { it })
.
l
Thanks Simon, I actually also think so and already implemented it in my project. 🙏