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:)
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 ActionpersonsList 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 F1 to 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.
a) (A => B ) => (M[A] => M[B]) // Monad
ReplyDeleteb) (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