angryziber / kotlin-puzzlers

A collection of Kotlin Puzzlers
419 stars 58 forks source link

Two puzzlers with contracts #63

Closed rpuxa closed 4 years ago

rpuxa commented 4 years ago
@UseExperimental(ExperimentalContracts::class)
inline fun runLambda(block: () -> Unit) {
    contract {
        callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
    }
}

fun getNothing(): Nothing {
    runLambda { throw UnsupportedOperationException("Functions cant return Nothing!") }
}

fun main() {
    val nothing: Nothing = getNothing()
    print("Hello ")
    print(nothing as String)
}

a) UnsupportedOperationException b) NullPointerException c) Hello TypeCastException d) Hello NullPointerException e) Will not compile

Correct answer is b, We trick the compiler using the AT_LEAST_ONCE contract (it means that lambda must be called at least once, but we don’t do this). Due to this getNothing() function returns null with no expection. Than compiler looks at the main function and thinks: "Hmm... getNothing() returns Nothing, but its impossible without throwing an exception. In this case I will remove all instructions after getNothing() invocation, because they are redundand, and write "throw null' instead of "return"

@UseExperimental(ExperimentalContracts::class)
inline fun runLambda(block: () -> Unit) {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
}

fun hello(): String {
    var hello: String
    runLambda { hello = "Hello " }
    return hello
}

fun world(): String {
    var world: String
    runLambda { world = "world!" }
    return world
}

fun main() {
    val result = hello().plus(world())
    println(result)
}

a) Hello world! b) Will not compile c) NullPointerException d) nullnull e) Not of the above

Correct answer is d. We trick compiler again, but now we used contracts to return uninitialized (null) String variable from NotNull function. But why we didnt get NullPointerException by invoking plus() function on null string? Because plus() compiles into StringBuilder like this:

    StringBuilder builder = new StringBuilder();
    builder.append(hello());
    builder.append(world());
    String result = builder.toString();

Thats why we got "nullnull"

angryziber commented 4 years ago

Thanks!