Functor Applicative and Monads

Written by vijay-aneryae | Published 2019/12/29
Tech Story Tags: kotlin | monads | functor | functional-programming | programming-top-story | programming | software-development | java

TLDR I am putting my understanding about functor,Applicative and Monad in Kotlin. I am using Kotlin to explain with examples with examples. A functor is a data structure which acts like a container holding a generic type. Applicative provides abstraction for how to apply a function that takes multiple arguments over multiple values. Functor is useful because they let us use map with collections, replacing for loops. Because Functormap returns another Functor with the result of the function passed to map I can chain multiple map functions together.via the TL;DR App

I am putting my understanding about functor,Applicative and Monad after spending few days to find out what monad is . Here is what I found. I am using Kotlin to explain with examples

What is functor

Functor can be defines in may ways as below
  • A functor is simply something that can be mapped over.
  • A functor is a data structure which acts like a container holding a generic type
Lets define the data type or container to hold value
class Functor<T> {    
 object None : Functor<Nothing>()     
 data class Some<out T>(val value: T) : Functor<T>()   
}
While we introduced functors as containers holding a value, sometimes
those containers can have interesting properties. For this reason,
functors are often described as “values in a context”.
When a value is wrapped in a context, you can’t apply a normal function to
it, and this is where map function comes in to apply normal function to
Wrapped Value
sealed class Functor<out A> {

    object None : Functor<Nothing>()

    data class Some<out A>(val value: A) : Functor<A>()

    inline infix fun <B> map(f: (A) -> B): Functor<B> = when (this) {
        is None -> this
        is Some -> Some(f(value))
    }
    companion object {
        fun <A> some(value: A): Functor<A> = Some(value)
    }


}
Lets apply function on the wrapped value
fun inc(value: Int): Int = value + 1
val increment = Functor(3).map {::inc} //OR
val increment = Functor(3).map {it + 1}
So this is functor, the container with map function .

Why Are Functors Useful?

  • Because we want to reuse code.
  • Functor generalizes how to map a function from one value to another.
  • Functors are useful because they let us use map with collections, replacing for loops
  • Chaining: Because Functor.map returns another Functor with the result of the function passed to map I can chain multiple map functions together

Applicative

Functor can only map a function which takes one argument. If we have a function that takes multiple arguments, we need Applicative.
Applicative provides abstraction for how to apply a function that takes multiple arguments over multiple values.
In Applicative, We can define an apply function for every type supporting Applicative, which knows how to apply a function wrapped in the context of the type to a value wrapped in the same context:
sealed class Functor<out A> {

    object None : Functor<Nothing>()

    data class Some<out A>(val value: A) : Functor<A>()

    inline infix fun <B> map(f: (A) -> B): Functor<B> = when (this) {
        is None -> this
        is Some -> Some(f(value))
    }
    companion object {
        fun <A> some(value: A): Functor<A> = Some(value)
    }

    infix fun <A, B> Functor<(A) -> B>.apply(f: Functor<A>): Functor<B> =
        when (this) {
            is None -> None
            is Some -> f.map(this.value)
        }
    
}
If you look carefully you will see that our operator only works in this specific order: Option(function) apply Option(value)
Examples 1 : Apply a function that takes two arguments to two wrapped values
fun curriedAddition(a: Int) = { b: Int ->
             a + b
         }
 Some(3) map ::curriedAddition map Some(2) // => COMPILER ERROR// Use applicative
 Some(3) map ::curriedAddition apply Some(2)
Apply triple product function:
fun tripleProduct(a: Int, b: Int, c: Int) = a * b * c

fun <A, B, C, D> curry(f: (A, B, C) -> D): (A) -> (B) -> (C) -> D = { a -> { b -> { c -> f(a, b, c) } } }
 
Some(3) map curry(::tripleProduct) apply Some(5) apply Some(4)
 // => Some(60)

Monads

Before we start with Monads, it is important to understand function composition in kotlin

Function Composition

  • Function composition is a technique to build functions using existing functions
  • Function Composition takes the result of invoking the right-hand function as the parameter for the left-hand function.
Take a Example of adding one and multiplying 3 to given number
val add : (Int) -> Int = {x -> x + 1 }
val mult : (Int) -> Int = { y -> y * 3}fun <A,B,C>composeF(f: (B) -> C, g: (A) ->B) {
   return { x -> f(g(x)) }
}
val addOneThenMul3 = composeF(::mul3, ::add1)
 
print(addOneThenMul3(2)) // output 9
Take another Example , you will get the input as String of two digit separated by comma, you have to divide first digit by second digit . For Input= "126,3" , output should be output= 126/3 = 4
  1. To solve this issue, we are going to breakdown problem in three function — Splitting — Parsing — Division
  2. We usually do this using composition as below
  3. val splitString : (String) -> Pair<Stirng,String> = {
            s ->  s.split(",").first() to s.split(",").last()
    }
    
    val parseToDouble : (Pair<String,String>) -> Pair<Double,Double> = {d -> d.first.toDouble() to  d.second.toDouble()}
    
    val division : (Pair<Double,Double>) -> Double = {d -> d.first/d.second}f
    
    un <A,B,C>composeF(f: (B) -> C, g: (A) ->B) {
       return { x -> f(g(x)) }
     }
     
    val result = composeF(composeF(division,parseToDouble),splitString)
    print(result("126,3")) // 42
    • Above use case for composition is not perfect , the reason because things can go wrong, like split function may return exception because these is no comma in between two digits, parse function may fail may be there is no number and eventually the result can fail
    • For any exceptions, the program can be partial , then what we can do
    • One way to do this is we compose these functions in a way to return “Embellished” results, That mean we can wrap the result in class (Functor) and that is where the term Monad comes

    Monads

    Monads are the mechanism which makes automatic composition of special kids of functions
    In another word, Monad is minimal amount of structure needed to overload functional composition in a way to perform extra computation on the intermediate value
    Understand Monad with above use case of split, parse divide
    To solve above above composition problem , we have to wrap the result in context of each function, so lets do it
    To wrap value we need Functor
    sealed class Result<out T> {
         object None : Result<Nothing>()
         data class Some<out T>(val value: T) : Result<T>()
     }
    And modify functions to return wrap result
    val split : (String) -> Result<Pair<String,String>> = { s ->
        val listString = s.split(",")
        Some(listString.first() to listString.last())
    }
    val parse : (Pair<String,String>) -> Result<Pair<Double,Double>> = {pair -> Some(pair.first.toDouble() to pair.second.toDouble()) }
    
    val divide : (Pair<Double,Double>) -> Result<Double> = {pair -> Some(pair.first.div(pair.second)) }
    Here we have modified functions to returns a wrapped value,
    We have to apply split function to parse function and then to divide function.
    Since the return type of each function is wrapped value and input parameter of each function is accepting pure values, we can't easily pass these function returning wrapped value to other functions .Here is monad come in picture,
    Monads apply a function that returns a wrapped value to a wrapped value using flatMap function
    So flatMap function looks like
    sealed class Result<out T> {
       object None : Result<Nothing>()
       data class Some<out T>(val value: T) : Result<T>()
       inline fun <B> flatMap(f: (T) -> Result<B>) : Result<B>  =
       when (this) {
             is None -> this
             is Some -> f(value)
        }           
    }
    Here we have written flatMap function on Functor
    Now you can apply function returning wrapped value to wrapped value
    val output = split("126,3").flatMap(parse).flatMap(divide)
    println(output) // will print 42
    So ins simple word, Modad is Functor that has flatMap
    Here is my github link for this example
    Thats it. I hope this article will help you understand idea of Functor and Monads and who they are comes together .
    This is my first article here and I hope you have enjoyed it ;) I’ll be glad for any feedback!

Published by HackerNoon on 2019/12/29