Alberto Ballano

@aballano

Kotlin Functors, Applicatives, And Monads in Pictures. Part 1/3

March 24th 2017
This is a translation of Functors, Applicatives, And Monads In Pictures from Haskell into Kotlin.
Actually this is a translation of another translation from Haskell to Swift.
I read through the original post and I found it really interesting for learning new concepts of FP, so I decided to do an additional translation (having also some fun in the way).
I also wanted to see how far can Kotlin get compared to Swift :)
If you enjoy this post be sure to say thanks to the author of the original version: Aditya Bhargava, @_egonschiele on Twitter.

Here’s a simple value:

And we know how to apply a function to this value:

Simple enough. Lets extend this by saying that any value can be in a context. For now you can think of a context as a box that you can put a value in:

Now when you apply a function to this value, you’ll get different results depending on the context. This is the idea that Functors, Applicatives, Monads, Arrows etc are all based on. The Option data type defines two related contexts:

Note: the pictures use Maybe (Just | None) from Haskell, which correspond to a custom Kotlin’s Option (Some | None) implementation.
sealed class Option<out A> {
object None : Option<Nothing>()
data class Some<out A>(val value: A) : Option<A>()
}

In a second we will see how function application is different when something is a Some(T)versus a None. First let’s talk about Functors!

Functors

When a value is wrapped in a context, you can’t apply a normal function to it:

This is where map comes in (fmap in Haskell). map is from the street, map is hip to contexts. map knows how to apply functions to values that are wrapped in a context. For example, suppose you want to apply a function that adds 3 to Some(2). Use map:

fun sumThree(n: Int) = n + 3

Option.Some(2).map(::sumThree)
// => Some(5)

or with a simple syntax using an anonymous lambda:

Option.Some(2).map { it + 3 }
// => Some(5)

Bam! map shows us how it’s done! But how does map know how to apply the function?

Just what is a Functor, really?

A Functor is any type that defines how map (fmap in Haskell) applies to it. Here’s how mapworks:

So we can do this:

Option.Some(2).map { it + 3 }
// => Some(5)

And map magically applies this function, because Option is a Functor. It specifies how map applies to Somes and Nones:

inline fun <B> map(f: (A) -> B): Option<B> = when (this) {
is None -> this
is Some -> Some(f(value))
}

Here’s what is happening behind the scenes when we write Option.Some(2).map { it + 3 }:

So then you’re like, alright map, please apply { it + 3 } to a None?

Option.None.map { it + 3 }
// => None

Well, there's a gotcha here since the code above doesn't compile. Why? Well because in this case None doesn't have a proper type, so you cannot do a plus with type Nothing. But it should be fine because you normally won't write that code but something like:

val option: Option<Int> = someCallThatMightReturnNone()
option.map { it + 3 }
// => None

Like Morpheus in the Matrix, map knows just what to do; you start with None, and you end up with None! map is zen. Now it makes sense why the Option type exists. For example, here’s how you work with a database record in a language without Option:

val post = Post.findByID(1)
return post?.title

But in Kotlin using the Option functor:

findPost(1).map(::getPostTitle)

If findPost(1) returns a post, we will get the title with getPostTitle. If it returns None, we will return None!

We can even define map as an infix function for (<$> in Haskell), and do this instead:

inline infix fun <B> map(f: (A) -> B): Option<B> { ... }

findPost(1) map ::getPostTitle
Note: we have to use just map because <$> wouldn't compile. Another option would be to override a common operator like / or *

Here’s another example: what happens when you apply a function to an array?

Arrays are functors too*!

*Basically Kotlin provides an extension function to all iterables in the form:

inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {..}

Okay, okay, one last example: what happens when you apply a function to another function?

{ a: Int -> a + 2 } map { a: Int -> a + 3 }
// => ???

Here's a function:

Here’s a function applied to another function:

The result is just another function!

typealias IntFunction = (Int) -> Int

infix fun IntFunction.map(g: IntFunction): IntFunction {
return { x -> this(g(x)) }
}

val foo = { a: Int -> a + 2 } map { a: Int -> a + 3 }
foo(10)
// => 15

So functions can be Functors too! When you use map on a function, you’re just doing function composition!

Well, that's it for today, I hope you got the idea about what's a Functor. Since the original post was pretty long, I'll continue in the next series with Applicatives. Now go try write some Functors in Kotlin!

Wanna play around with the code? Take a look at https://github.com/aballano/FAM-Playground

Want more? Go try applicatives in the second part!

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMI family. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!

More by Alberto Ballano

More Related Stories