I'm wondering about the `contract` specified for `...
# arrow
h
I'm wondering about the
contract
specified for
Either::getOrNull
. It seems incorrect in case
B
(right) is nullable. For reference:
Copy code
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..
Copy code
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))
?
1
y
Huh, I never thought about that. Not sure why your example isn't working btw. I'll try to replicate this issue!
It seems like the contract doesn't even work yeah! It means we can easily change it though
3 things: • contracts still don't work if coming from a 3rd-party library • Arrow's Kotlin version a little bit behind your and my IDE Kotlin version • Thus, I have to redefine
getOrNull
and use a new Kotlin version to see this issue, but you're correct, this compiles and fails with a CCE at runtime:
Copy code
@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 }
}
h
Ah thanks for looking into this! Great that the trouble in ~re~producing the bug means a whole lot less trouble correcting it 🙂
1
y
d
Wait, contracts in libraries aren't honored?
y
callsInPlace
is.
implies
seems to not be yet. I know that has been a long standing bug
d
Ah, okay.