Monday 28 May 2012

Rework: Monads in Scala - 1


I have to spend more time than other (smart guys) trying to get simple topics. As a profit - getting clean description for myself - using simple (clean for non math geek) vision. I've read hundred of articles/examples/... + done few presentations on the same topic. Just want to finalize it and forget:)

As a conclusion generalization of my previous self-study experience I would kindly ask James Iry:
Usually articles that start with words like "monad" and "functor" quickly devolve into soup of Greek letters. That's because both are abstract concepts in a branch of mathematics called category theory and explaining them completely is a mathematical exercise.
JAMES IRY


But there must be possibility to avoid complexity, to allow understanding the topic while reading in the bus or 3 minutes before falling asleep. U r welcome to the world of Monads in Scala.





Session 1: Monad, Functor and Applicative

1) Install environment:

- Download Intellij Idea Community Edition
- Don't forget about Scala plugin
- Clone repository from https://stasimus@github.com/stasimus/monads-in-scala-sessions.git
- Create new Idea Project:
  • New Project ...
  • Import From External Sources ...
  • Maven
  • Path to project root
  • JDK and other Buttons by default

2) Intro

Just giving example of Monads in Action

personsList filterBy {_.age > 31} map {_.team}
that later pushes to implement structures like

List("one", "two", "three", "four", "five") groupBy (_ size)

Looks nice and readable, isn't it?)

3) Theory

Imagine the task (world) where U have only: 
  • container type M[_] (_ means any), which is simple wrapper around business types A and B
  • function, which helps U to get target type B from A

a) (A => B) => (M[A] => M[B])
b) (A => M[B] ) => (M[A] => M[B])
c) (M [A => B]) => (M[A] => M[B])

All this have the names

a) (A => B ) => (M[A] => M[B]) // Monad
b) (A => M[B] ) => (M[A] => M[B]) //Functor
c) (M [A => B]) => (M[A] => M[B]) //Applicative


Don't concentrate at this moment why do we need these structures
Let's try to rephrase them:
Monad: Function (A => B) applied on Container M with value A returns Container with value B (M[B])
Functor: Funct (A => M[B]) already knows how to wrap value to Container, applied on M[A] returns M[B]
Applicative: Function wrapped in Container, applied to Container M[A] returns M[B]
Please don't read it twice. Lets do a coding, determine container:


     class Box[T](val value: T)

initializing real instance example:

    val box = new Box("Testing")

Lets imagine situation when we want to proceed logic that gets A type and returns some processing result type B from A, that must be incapsulated in the same container type Box (Doesn't change the kind of monad, but may change it parameterized type, Box[A] can become Box[B] but should not become Container[B]). Giving it the name map:

    def map[A, B](func: A => B): Box[A] => Box[B] =
            (box: Box[A]) => new Box(func(box.value))

U did a first Functor! From this map is Functor and Hight Order Function that gets a business function F1(A => B) as a parameter and returns a Function F2(Box[A] => Box[B]that can be used to apply F1 to existed Box[A].

Real life example: get the size of String, long (readable) form solution example:

      val in = new Box("123456789")
      val rawFunc: (String) => Int = (str: String) => str.size
      val mapFunc:(Box[String]) => Box[Int] = Box.map(rawFunc)
      val res:Box[Int] = mapFunc(in) // Box[Int](9)


While Monads shines when they are combinable it's not a time to introduce this, please be patient while next lesson we have to create new transformation flatMap:


    def flatMap[A, B](func: A => Box[B]): Box[A] => Box[B] =
          (box: Box[A]) => func(box.value)

U did a first Monad! From this flatMap is Monad that gets a business function F1(A => Box[B]) as a parameter and returns a Function F2(Box[A] => Box[B]that can be used to apply Fto existed Box[A].
Same example:

    val in = new Box("12345678")
    val rawFunc = (value: String) => new Box(value.size)
    val flatMapFunc = Box.flatMap(rawFunc)
    val res = flatMapFunc(in) // Box[Int](8)

Last task for today: imagine that 'business' function is wrapped with Container Box[A => B], to be able to work with this construction you should implement last one transformation:


    def apply[A, B](func: Box[A => B]): Box[A] => Box[B]
          = (box: Box[A]) => new Box(func.value(box.value))

apply like this is called Applicative! This is 3-rd and last basic transformation for today. Code without Scala's sugar is here:

    val in = new Box(12345678)

    val rawFunc = new Box((value: Int) => value.toString)
    val applyFunc = Box.apply(rawFunc)
    val res = applyFunc(in) // Box[String]("12345678")


Enjoy, it's still not complex topic,
indeed we skiped a lot of math background, reasoning why to use and main magic: Scala's approach. In the next Lesson we wil try to drive into Monadic features in Scala, where Object Oriented approach is mixed with Function Oriented.








1 comment:

  1. a) (A => B ) => (M[A] => M[B]) // Monad
    b) (A => M[B] ) => (M[A] => M[B]) //Functor
    c) (M [A => B]) => (M[A] => M[B]) //Applicative

    Shouldn't it be like that?

    a) (A => B ) => (M[A] => M[B]) // Functor
    b) (A => M[B] ) => (M[A] => M[B]) //Monad

    ReplyDelete