How to assure a type using Kotlin's contracts in an infix function?

35 views Asked by At

Based on this answer I wanted to create an infix version of a function which checks a variable type.
However, it seems to be ignored by the compiler. Please see the example code:

@OptIn(ExperimentalContracts::class)
inline infix fun <SELF, reified TYPE : Class<out Any>> SELF.isType(type: TYPE): Boolean {
    contract { returns(true) implies (this@isType is TYPE) }
    return type.isInstance(this)
}

fun demo() {
    // we know it can be only String
    val someValue: Any = if (System.currentTimeMillis() <= 1) 12345 else "some-string"
    val someType = String::class.java

    // I would expect that [isInstance] implies a type inside the "if" block, but it doesn't.
    // Without "as String" it's a compile error.
    if (someType.isInstance(someValue)) {
        println("length is " + (someValue as String).length)
    }

    // That's why I want to create a custom infix function "isType" with Kotlin contracts.
    // But still, without "as String" it's a compile error.
    if (someValue isType someType) {
        println("length is " + (someValue as String).length)
    }
}

What could be the problem there?

1

There are 1 answers

0
broot On

There are two reasons for this.

First, you made a mistake in your code. You say someValue is a TYPE, but TYPE is Class<String>. Even if your smart cast worked correctly, you cast someValue to Class<String> and you planned to cast it to String. Fixed code:

inline infix fun <SELF, reified TYPE> SELF.isType(type: Class<TYPE>): Boolean {

Second, apparently, there is a bug in the Kotlin compiler and smart casts don't work if using the infix syntax:

if (someValue isType someType) {
    println("length is " + someValue.length) // doesn't compile
}
if (someValue.isType(someType)) {
    println("length is " + someValue.length) // compiles
}