# Extending Kotlin's Null-Safety with Monad Comprehension

[Kotlin](https://kotlinlang.org/) has a very decent null-safety baked in; and even better, it is a part of its type system.

```kotlin

private fun easy1(str: String?): String {
    return str?.let {
        return "$str is so easy"
    } ?: "Not so easy after all"
}

// We can easily call this function with a null or String value
@Test
fun easyTest() {
    assertEquals(easy1(null), "Not so easy after all")
    assertEquals(easy1("Life"), "Life is so easy")
}
```

Looking at the signature of `easy1`, it accepts a parameter of type `String?`**.** The `?` means that the parameter can also be null. Inside its function body, we can see the expression `str?.let{ <block> } ?: <expr>`, which means, that if `str` is not null, run `block` else return `expr`. There is no direct and safe access to `str` without making sure that it is non-null, this in itself is null-safety.

Compare the above example with the following

```kotlin
private fun easy2(str: String): String {
    return "$str is so easy"
}

@Test
fun easy2Test() {
    // compile error: Null can not be a value of a non-null type String
    assertEquals(easy2(null), "Not so easy after all")
    assertEquals(easy2("Life"), "Life is so easy")
}
```

Here, the compiler does not even allow us to call `easy2`, because it would not take a null value.

But, what happens when we need to use a couple of nullable values, and only if they are non-nullable. Given:

```kotlin
data class Person(val name: String, val age: Int, val email: String)

interface PersonService {
    fun getName(): String?
    fun getAge(): Int?
    fun getEmail(): String?
    fun createPerson(name: String, age: Int, email: String): Person
}
```

In the listing above, we have a `Person` data class and an interface that describes what a `PersonService` should look like. Let's try to create a new `Person` using an implementation of the `PersonService`

```kotlin
fun createPerson() {
    return personService.getName()?.let { name ->
        personService.getAge()?.let { age ->
            personService.getEmail()?.let { email ->
                personService.createPerson(name, age, email)
            }
        }
    }
}
```

The `?.let` chains gets out of hand very quickly, and it can be a bit hard to keep track of things. We can build on Kotlin's inbuilt null-safety to provide a more ergonomic way to use nullable values.

### First attempt — zip method

```kotlin
fun<T, U> T?.zip(other: U?): Pair<T, U>?  = 
    this?.let { other?.let { Pair(this, other) } }
```

Taking advantage of the nullable `let` bindings, we provide an abstraction that wraps two nullable values into a nullable `Pair` of non-null values. Let's see the usage of this method:

```kotlin
@Test
fun zipTest() {
    var v1: String? = null
    var v2: Int? = null

    val res1 = v1.zip(v2)?.let { (a, b) -> "v1 is $a and v2 is $b" }
    assertEquals(res1, null)

    v1 = "Hi"
    v2 = 4
    val res2 = v1.zip(v2)?.let { (a, b) -> "v1 is $a and v2 is $b" }
    assertEquals(res2, "v1 is Hi and v2 is 4")

    v2 = null
    val res3 = v1.zip(v2)?.let { (a, b) -> "v1 is $a and v2 is $b" }
    assertEquals(res3, null)
}
```

We start out with two variables, `v1` and `v2`, that are nullable. At `res1`, `v1` is zipped with `v2`, and both are `null`, so the nullable `let` binding block doesn't get evaluated because `zip` returns `null`. The nice part of the `zip` method is that, inside the `let` block, that `a` and `b` are both non-nullable. At `res2`, both `v1` and `v2` has been set, so the `let` block gets evaluated. At `res3`, `v2` has been set back to `null`, and even though `v1` is not `null`, `zip` still returns null. Let's apply this to our person creation problem:

```kotlin
@Test
fun createPerson2() {
    val person: Person? =
        personService.getName()
            .zip(personService.getAge())
            .zip(personService.getEmail())
            ?.let { (nameAndAgePair, email) ->
                val (name, age) = nameAndAgePair
                personService.createPerson(name, age, email)
            }
}
```

This comes with a bit of mixed feelings. Already we get some breath of fresh air, in the sense, that we got rid of the nested `let` blocks, but combining three nullable values results in a pair of a pair and a value, in this case, a `Pair<Pair<String, Int>, String>` type. We then need to do multiple destructuring to get the values out.

> We can also not even use destructuring at all, and just access items in the `Pair` with the `first` and `second` getters, but we quickly get to the point where we need to do `pair.first.first`, which is not as good either.

### Second attempt — the `NullableScope` and the `bind` method (a.k.a. Monad Comprehension)

```kotlin
class NullableScope {
    fun<T> T?.bind(): T {
        return  this ?: throw Exception()
    }
}

inline fun<T> nullable(block: NullableScope.() -> T): T? {
    return with(NullableScope()) {
        try {
            block()
        } catch (e: Exception) {
            null
        }
    }
}
```

These are few lines of code, but if you are not used to these kinds of things in Kotlin, it might look very daunting at first. So let's take them apart.

Firstly, we defined a class `NullableScope`, which defines a method `T?.bind()`, which returns `this` or throws an exception if `this` is `null`. `T` is a generic type parameter and `T?` is saying that the generic type parameter can be nullable. So we can call `bind` on nullable values as well as non-nullable values. This class is used as a scope, within which, any type, `T` has access to the `bind` method. So outside the `NullableScope`, the `bind` method, on any type, is not defined.

Secondly, we define a function `nullable` with a generic parameter `T`. `nullable` accepts a block that evaluates to `T`, as a parameter, but this block is a special kind. It is not just `() -> T`, rather `NullableScope.() -> T`, which roughly means that this block can only be resolved only in a `NullableScope` context and when evaluated should return a value of type `T`. The `nullable` method itself returns a nullable `T` (`T?`).

> using `nullable` as the function name makes it hard to read the last paragraph but bear with me. This sacrifice will be worth it

Then, inside the body of the function, we use the Kotlin `with` keyword to provide the context `NullableScope` and provide a block that wraps the evaluation of the `block` parameter in a `try catch` block. If any exception is thrown, it is caught and `null` is returned. It is essential that we wrap this in a `try catch` block, since our `bind` method is expected to throw an exception whenever it is called on a `null` value. Let's see this in action:

```kotlin
@Test
fun nullableScopeStopsAfterFirstBindOnNull() {
    val v1: String? = null
    val v2: Int? = null

    val v5 = nullable {
        val v3: String = v1.bind()
        val v4: Int = v2.bind()
        v3.slice(0..v4)
    }
    assertEquals(v5, null)
}
```

`v5` uses the `nullable` function to wrap a block, where nullable values can be used as non-nulls by calling bind on them. `v5` evaluates to `String?` type. This looks very readable and can be processed linearly.

Let's see an example where all variables resolve to a non-nullable

```kotlin
@Test
fun nullableScopeEvaluatesToValueIfNoNullValueWasBound() {
    var v1: String? = "value"
    val v2: Int? = 2

    val v5 = nullable {
        val v3 = v1.bind()
        val v4 = v2.bind()
        v3.slice(0..v4)
    }

    assertEquals(v5, "val")
}
```

Super!

But, what if we have some other computation inside the `nullable` block, that we would want to throw an exception if something goes wrong? Like below:

```kotlin
@Test
fun `nullable scope does lets exception to be thrown`() {
    val v1: String? = null
    val v2: Int? = null
    var count = 0
    val v5 = {
        nullable {
            if (count == 0) {
                throw Exception()
            }
            val v3 = v1.bind()
            val v4 = v2.bind()
            v3.slice(0..v4)
        }

    }

    assertEquals(count, 0)
   // fails with `Expected Exception to be thrown, 
   // but nothing was thrown`
    assertThrows<Exception> { v5() }
}
```

We can find the culprit for this in our implementation of the `nullable` function. Here it is again:

```kotlin
inline fun<T> nullable(block: NullableScope.() -> T): T? {
    return with(NullableScope()) {
        try {
            block()
        } catch (e: Exception) {
            null
        }
    }
}
```

We catch every possible exception and just return null. We can do better.

```kotlin
private class NullableException: Exception()

class NullableScope {
    fun<T> T?.bind(): T {
        return  this ?: throw NullableException()
    }
}

inline fun<T> nullable(block: NullableScope.() -> T): T? {
    return with(NullableScope()) {
        try {
            block()
        } catch (e: NullableException) {
            null
        }
    }
}
```

Furthermore, we introduce a private Exception class `NullableException`, made private, so it cannot be used nor extended beyond the scope of this module. Then replace the `Exception` we threw inside `bind` and the one we caught inside the `nullable` function body, with it. This way, the `nullable` block can only return `null` if `NullableException` is thrown, therefore every other exception can be thrown successfully. And our test passes now 🔥

```kotlin
@Test
fun `nullable scope does lets exception to be thrown`() {
    val v1: String? = null
    val v2: Int? = null
    var count = 0
    val v5 = {
        nullable {
            if (count == 0) {
                throw Exception()
            }
            val v3 = v1.bind()
            val v4 = v2.bind()
            v3.slice(0..v4)
        }

    }

    assertEquals(count, 0)
   // passes ✅
    assertThrows<Exception> { v5() }
}
```

Let's try to use this our new invention with the person creation problem

```kotlin
fun createPerson3() {
    val person: Person? = nullable { 
        val name = personService.getName().bind()
        val age = personService.getAge().bind()
        val email = personService.getEmail().bind()
        
        personService.createPerson(name, age, email)
    }
}
```

Speaking of breath of fresh air 😌. Here, we have avoided the need for nesting, and that of multiple chaining. It just reads like plain ol' imperative code.

Let's go a bit above and beyond by reimplementing the `zip` function with the `nullable` block.

```kotlin
fun<T, U> T?.zip(other: U?): Pair<T, U>?  = nullable {
    Pair(this@zip.bind(), other.bind())
}
```

This pattern is widespread in a lot of other programming languages, like Haskell's do block, Rust's `?` operator, Scala's `for` block and a lot more. [Arrow-kt's raise.nullable](https://github.com/arrow-kt/arrow//blob/main/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt#L51) provides this functionality, so if you have arrow-kt as a dependency, you can use that. If not, you now know how to implement one by yourself. Et voilà!

Code used for this post can be found on GitHub [here](https://github.com/ikezedev/extending-kotlin-null-safetly)

> Going through the code on github, you would notice the use of @sample block in the kdoc documentation comments. As someone very passsionate about good and tested documentations, I encourage everyone to use this in their documentation comments. It makes it easy for anyone using your functions to easily glance over how to use them without having to dive into your source code. Simply put, documentation with just explanation text is not enough, add useful examples as well.

> I have a couple of reviews from Reddit and other places, that I would be addressing below, later in the day. Thanks for reading and your comments
