Huib Donkers
08/14/2025, 5:18 PMcontract
specified for Either::getOrNull
. It seems incorrect in case B
(right) is nullable.
For reference:
public fun getOrNull(): B? {
contract {
returns(null) implies (this@Either is Left<A>)
returnsNotNull() implies (this@Either is Right<B>)
}
return getOrElse { null }
}
But maybe I'm misunderstanding something about contracts (or arrow). I'm actually unable to get my examples to work, seems my setup is wacky..
fun foo(either: Either<String, Int?>) = if (either.isLeft()) "Left(${either.value})" else "Right(${either.value})" // IntelliJ highlights as of this compiles and either is smart cast to Left and Right, but when compiling it gives the error "Unresolved reference: value"
fun bar(either: Either<String, Int?>) = if (either.getOrNull() == null) "Left(${either.value})" else "Right(${either.value})" // IntelliJ highlighting and compiler agree: no smart casting (but what does the contract do then?)
But regardless of my potentially wacky setup, what should happen when I call bar(Either.Right(null))
?Youssef Shoaib [MOD]
08/14/2025, 6:58 PMYoussef Shoaib [MOD]
08/14/2025, 7:21 PMYoussef Shoaib [MOD]
08/14/2025, 7:40 PMgetOrNull
and use a new Kotlin version to see this issue, but you're correct, this compiles and fails with a CCE at runtime:
@Test
fun getOrNullNullable() = runTest {
val either: Either<String, Int?> = Right(null)
if (either.gon() == null) {
val left: Either.Left<*> = either
left.value
}
}
fun <A, B> Either<A, B>.gon(): B? {
contract {
returns(null) implies (this@gon is Left<A>)
returnsNotNull() implies (this@gon is Right<B>)
}
return getOrElse { null }
}
Huib Donkers
08/14/2025, 8:39 PMYoussef Shoaib [MOD]
08/14/2025, 9:27 PMDaniel Pitts
08/16/2025, 5:21 PMYoussef Shoaib [MOD]
08/16/2025, 5:42 PMcallsInPlace
is. implies
seems to not be yet. I know that has been a long standing bugDaniel Pitts
08/16/2025, 7:24 PM