paint-brush
Kotlin Functors, Applicatives, And Monads in Pictures. Part 1/3by@aballano
16,735 reads
16,735 reads

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

by Alberto BallanoMarch 25th, 2017
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Here’s a simple value:

Company Mentioned

Mention Thumbnail
featured image - Kotlin Functors, Applicatives, And Monads in Pictures. Part 1/3
Alberto Ballano HackerNoon profile picture

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

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

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 -> thisis 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

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!


Kotlin Functors, Applicatives, And Monads in Pictures. Part 2/3_This is a translation of Functors, Applicatives, And Monads In Pictures from Haskell into Kotlin._medium.com