Grokking Monads in Scala St. Louis Lambda Lounge August 5, 2010 Tim Dalton Senior Software Engineer Object Computing Inc.
Monads Are… Just a monoid in the category of endofunctors.  Like “duh”!
Monads Are… <ul><li>A way to structure computations </li></ul><ul><li>A strategy for combining computations into more comp...
Monads Are… <ul><ul><li>In Haskell, two core functions </li></ul></ul><ul><li>(>>=)  :: m a -> (a -> m b) -> m b </li></ul...
Haskell Monads <ul><ul><li>Haskell supports “do notation” for chaining monadic computations: </li></ul></ul><ul><ul><ul><l...
Scala &quot;For comprehensions&quot; for (i <- 1 to 5) yield i scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3...
De-sugarized For comprehensions (1 to 5).map(identity) scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5) ...
A Monadic Trait <ul><li>abstract trait M[A] {  </li></ul><ul><li>def unit[B] (value : B):M[B] </li></ul><ul><li>def map[B]...
Simplest Monad – Identity case class Identity[A](value:A) { def map[B](f:(A) => B) = Identity(f(value)) def flatMap[B](f:(...
AST Evaluator <ul><li>An evaluator for an Abstract Syntax Tree (AST) is going to implemented for illustration </li></ul><u...
AST Evaluator – Identity object IdentityEvaluator extends  EvaluatorTrait[Term, Identity[Int]]  { def eval(term: Term) = t...
Useful Monad - Option <ul><li>sealed abstract class Option[+A] extends Product { </li></ul><ul><li>def map[B](f: A => B): ...
Usefulness of Option val areaCodes = Map( &quot;Fenton&quot; -> 636, &quot;Florissant&quot; -> 314, &quot;Columbia&quot; -...
Usefulness of Option println(personAreaCode(&quot;Moe&quot;)) Some(573) println(personAreaCode(&quot;Schemp&quot;)) None p...
AST Evaluator - Option object OptionDivide  extends ((Option[Int], Option[Int]) => Option[Int]) { def apply(a:Option[Int],...
AST Evaluator - Option println(eval(Divide(Divide(Constant(1972),Constant(2)), Constant(23)))) Some(42) println(eval(Divid...
“ Wonkier” Monad – State object State { def unit[S,A](a:A) = new State((s:S) => (s, a)) } case class State[S, A](val s:S =...
State Monad val add = (x:Int, y:Int) =>  State[List[String], Int]((s:List[String]) => { ((x + &quot; + &quot; + y + &quot;...
State Monad – No Sugar val f = add(2,2).flatMap{ x1 =>  sub(x1, 5).flatMap { x2 =>  add(x2,2) }   }.map(identity) val resu...
AST Evaluator - State object StateEvaluator  extends EvaluatorTrait[Term, State[Int, Option[Int]]]  {  def eval(term: Term...
Summary <ul><li>Scala supports monadic style of computation to emulate features of “purer” functional programming language...
Discussion  Can monads ever be “mainstream” ?
Links James Iry – “Monads are Elephants” http://james-iry.blogspot.com/2007/09/monads-are-elephants-part-1.html http://jam...
Upcoming SlideShare
Loading in...5
×

Grokking Monads in Scala

3,342

Published on

Presentation slide from St. Louis Lambda Lounge presentation on August 5th 2010.

Published in: Technology, Education
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
3,342
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
76
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide
  • I personally find the spacesuit metaphor the most helpful
  • Return == unit
  • Return == unit
  • flatMap for Scala sequences and lists implement the List Monad
  • No unit method is implemented here. Oftentimes constructors act has units. There are some high-level functions that can operate over different types of monads that often need a unit function Identity pretty much put the “astronaut in another suit”
  • State in this case sums the constants (kind of contrived)
  • State in this case sums the constants (kind of contrived)
  • State in this case sums the constants (kind of contrived)
  • Grokking Monads in Scala

    1. 1. Grokking Monads in Scala St. Louis Lambda Lounge August 5, 2010 Tim Dalton Senior Software Engineer Object Computing Inc.
    2. 2. Monads Are… Just a monoid in the category of endofunctors. Like “duh”!
    3. 3. Monads Are… <ul><li>A way to structure computations </li></ul><ul><li>A strategy for combining computations into more complex computations </li></ul><ul><li>(credit: Julien Wetterwald) </li></ul>
    4. 4. Monads Are… <ul><ul><li>In Haskell, two core functions </li></ul></ul><ul><li>(>>=) :: m a -> (a -> m b) -> m b </li></ul><ul><li>return :: a -> m a </li></ul><ul><ul><li>Monad Laws </li></ul></ul><ul><ul><ul><li>Left Unit: </li></ul></ul></ul><ul><ul><ul><ul><li>(return a) >>= k = k a </li></ul></ul></ul></ul><ul><ul><ul><li>Right Unit </li></ul></ul></ul><ul><ul><ul><ul><li>m >>= (return) = m </li></ul></ul></ul></ul><ul><ul><ul><li>Associative </li></ul></ul></ul><ul><ul><ul><ul><li>m >>= (a -> (k a) >>= (b -> h b)) = </li></ul></ul></ul></ul><ul><li>(m >>= (a -> k a)) >>= (b -> h b) </li></ul>
    5. 5. Haskell Monads <ul><ul><li>Haskell supports “do notation” for chaining monadic computations: </li></ul></ul><ul><ul><ul><li>do { x <- Just (3+5) </li></ul></ul></ul><ul><ul><ul><li>y <- Just (5*7) </li></ul></ul></ul><ul><ul><ul><li>return (x-y) </li></ul></ul></ul><ul><ul><ul><li>} </li></ul></ul></ul><ul><ul><li>Which is “sugar” for: </li></ul></ul><ul><ul><ul><li>Just (3+5) >>= x -> </li></ul></ul></ul><ul><ul><ul><li>Just (5*7) >>= y -> </li></ul></ul></ul><ul><ul><ul><li>return (x-y) </li></ul></ul></ul><ul><ul><li>Should evaluate to: </li></ul></ul><ul><ul><li> Just(-27) </li></ul></ul>
    6. 6. Scala &quot;For comprehensions&quot; for (i <- 1 to 5) yield i scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5) for (i <- 1 to 5 if i % 2 == 0) yield i scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4) for (i <-1 to 5 if i % 2 == 0) { print (i + &quot; &quot; ) } 2 4 for (i <-1 to 5 if i % 2 == 0; j <- 1 to 5 if j % 2 != 0) yield ( i * j ) scala.collection.immutable.IndexedSeq[Int] = Vector(2, 6, 10, 4, 12, 20) for (i <-1 to 5 if i % 2 == 0; j <- 1 to 5 if j % 2 != 0; k <- 1 to 5) yield ( i * j / k ) scala.collection.immutable.IndexedSeq[Int] = Vector(2, 1, 0, 0, 0, 6, 3, 2, 1, 1, 10, 5, 3, 2, 2, 4, 2, 1, 1, 0, 12, 6, 4, 3, 2, 20, 10, 6, 5, 4)
    7. 7. De-sugarized For comprehensions (1 to 5).map(identity) scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5) (1 to 5).filter{_ % 2 == 0}.map(identity) scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4) (1 to 5).filter{_ % 2 == 0}.foreach { i => print (i + &quot; &quot; ) } 2 4 (1 to 5).filter{_ % 2 == 0}.flatMap { i => (1 to 5).filter{_ % 2 != 0}.map{ j => i * j } } scala.collection.immutable.IndexedSeq[Int] = Vector(2, 6, 10, 4, 12, 20) (1 to 5).filter{_ % 2 == 0}.flatMap { i => (1 to 5).filter{_ % 2 != 0}.flatMap{ j => (1 to 5).map{ k => i * j / k } } } scala.collection.immutable.IndexedSeq[Int] = Vector(2, 1, 0, 0, 0, 6, 3, 2, 1, 1, 10, 5, 3, 2, 2, 4, 2, 1, 1, 0, 12, 6, 4, 3, 2, 20, 10, 6, 5, 4)
    8. 8. A Monadic Trait <ul><li>abstract trait M[A] { </li></ul><ul><li>def unit[B] (value : B):M[B] </li></ul><ul><li>def map[B](f: A => B) : M[B] = flatMap {x => unit(f(x))} </li></ul><ul><li>def flatMap[B](f: A => M[B]) : M[B] </li></ul><ul><li>} </li></ul><ul><ul><li>Scala flatMap correlates to Haskell’s bind (>>=) </li></ul></ul><ul><ul><li>Scala map can be expressed in terms of flatMap or vice versa </li></ul></ul><ul><ul><ul><ul><li>Some implementations use map and flatten </li></ul></ul></ul></ul><ul><ul><ul><ul><li>Haskell convention for flatten is “join” </li></ul></ul></ul></ul><ul><ul><li>Trait is used for illustration. There are many ways to implement monads in Scala. </li></ul></ul>
    9. 9. Simplest Monad – Identity case class Identity[A](value:A) { def map[B](f:(A) => B) = Identity(f(value)) def flatMap[B](f:(A) => Identity[B]) = f(value) }
    10. 10. AST Evaluator <ul><li>An evaluator for an Abstract Syntax Tree (AST) is going to implemented for illustration </li></ul><ul><ul><li>Scala Trait for evaluator: </li></ul></ul><ul><ul><li>trait EvaluatorTrait[A,M] { </li></ul></ul><ul><ul><li>def eval(a:A):M; </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><ul><li>M will be a Monadic type </li></ul></ul></ul><ul><li>Example used derives from Phillip Wadler’s “Monads for functional programming” paper. </li></ul><ul><ul><li>Can only handle integer constants and division operations </li></ul></ul><ul><ul><li>sealed abstract class Term() </li></ul></ul><ul><ul><li>case class Constant(value:Int) extends Term </li></ul></ul><ul><ul><li>case class Divide(a:Term, b:Term) extends Term </li></ul></ul>
    11. 11. AST Evaluator – Identity object IdentityEvaluator extends EvaluatorTrait[Term, Identity[Int]] { def eval(term: Term) = term match { case Constant(x) => Identity(x) case Divide(a,b) => for (bp <- eval(b); ap <- eval(a)) yield (ap/bp) } println(eval(Divide(Divide(Constant(1972),Constant(2)), Constant(23)))) Identity(42) println(eval(Divide(Constant(1),Constant(0)))) Exception in thread &quot;main&quot; java.lang.ArithmeticException: / by zero
    12. 12. Useful Monad - Option <ul><li>sealed abstract class Option[+A] extends Product { </li></ul><ul><li>def map[B](f: A => B): Option[B] = </li></ul><ul><li>if (isEmpty) None else Some(f(this.get)) </li></ul><ul><li>def flatMap[B](f: A => Option[B]): Option[B] = </li></ul><ul><li>if (isEmpty) None else f(this.get) </li></ul><ul><li>} </li></ul><ul><li>final case class Some[+A](x: A) extends Option[A] { </li></ul><ul><li>def isEmpty = false </li></ul><ul><li>def get = x </li></ul><ul><li>} </li></ul><ul><li>case object None extends Option[Nothing] { </li></ul><ul><li>def isEmpty = true </li></ul><ul><li>def get = throw new NoSuchElementException(&quot;None.get&quot;) </li></ul><ul><li>} </li></ul><ul><li>Also referred to as the Maybe or Failure monad. </li></ul><ul><ul><li>Haskell supports Just/Nothing that correlates to Some/None in Scala </li></ul></ul>
    13. 13. Usefulness of Option val areaCodes = Map( &quot;Fenton&quot; -> 636, &quot;Florissant&quot; -> 314, &quot;Columbia&quot; -> 573 ) val homeTowns = Map( &quot;Moe&quot; -> &quot;Columbia&quot;, &quot;Larry&quot; -> &quot;Fenton&quot;, &quot;Curly&quot; -> &quot;Florissant&quot;, &quot;Schemp&quot; -> &quot;St. Charles” ) def personAreaCode(person:String) = for (homeTown <- homeTowns.get(person); areaCode <- areaCodes.get(homeTown)) yield (areaCode)
    14. 14. Usefulness of Option println(personAreaCode(&quot;Moe&quot;)) Some(573) println(personAreaCode(&quot;Schemp&quot;)) None println(personAreaCode(&quot;Joe&quot;)) None println( for (areaCode <- areaCodes if areaCode._2 == 314; stoogeHome <- homeTowns if stoogeHome._2 == areaCode._1) yield stoogeHome._1 ) List(Curly) Look Mom, No null checks !!!
    15. 15. AST Evaluator - Option object OptionDivide extends ((Option[Int], Option[Int]) => Option[Int]) { def apply(a:Option[Int], b:Option[Int]) = for (bp <- b; ap <- if (bp != 0) a else None) yield (ap/bp) } object OptionEvaluator extends EvaluatorTrait[Term, Option[Int]] { def eval(term: Term) = term match { case Constant(x) => Some(x) case Divide(a,b) => OptionDivide(eval(a), eval(b)) } }
    16. 16. AST Evaluator - Option println(eval(Divide(Divide(Constant(1972),Constant(2)), Constant(23)))) Some(42) println(eval(Divide(Constant(1),Constant(0)))) None
    17. 17. “ Wonkier” Monad – State object State { def unit[S,A](a:A) = new State((s:S) => (s, a)) } case class State[S, A](val s:S => (S, A)) { def map[B](f: A => B): State[S,B] = flatMap((a:A) => State.unit(f(a))) def flatMap[B](f: A => State[S,B]): State[S,B] = State((x:S) => { val (a,y) = s(x) f(y).s(a) }) }
    18. 18. State Monad val add = (x:Int, y:Int) => State[List[String], Int]((s:List[String]) => { ((x + &quot; + &quot; + y + &quot; = &quot; + (x + y)) :: s, (x + y)) }) val sub = (x:Int, y:Int) => State[List[String], Int]((s:List[String]) => { ((x + &quot; - &quot; + y + &quot; = &quot; + (x - y)) :: s, (x - y)) }) val f = for (x1 <- add(2 , 2); x2 <- sub(x1, 5); x3 <- add(x2, 2)) yield (x3) val result = f.s(Nil) println(&quot;log = &quot; + result._1.reverse) log = List(2 + 2 = 4, 4 - 5 = -1, -1 + 2 = 1) println(&quot;result = &quot; + result._2) result = 1
    19. 19. State Monad – No Sugar val f = add(2,2).flatMap{ x1 => sub(x1, 5).flatMap { x2 => add(x2,2) } }.map(identity) val result = f.s(Nil) println(&quot;log = &quot; + result._1.reverse) log = List(2 + 2 = 4, 4 - 5 = -1, -1 + 2 = 1) println(&quot;result = &quot; + result._2) result = 1
    20. 20. AST Evaluator - State object StateEvaluator extends EvaluatorTrait[Term, State[Int, Option[Int]]] { def eval(term: Term) = term match { case Constant(x) => State((s:Int) => (s + x, Some(x))) case Divide(a,b) => for ( evala <- eval(a); evalb <- eval(b)) yield OptionDivide(evala, evalb) } println(eval(Divide(Divide(Constant(1972),Constant(2)), Constant(23))).s(0)) (1997,Some(42)) println(eval(Divide(Constant(20),Constant(0))).s(0)) (20,None)
    21. 21. Summary <ul><li>Scala supports monadic style of computation to emulate features of “purer” functional programming languages </li></ul><ul><li>For comprehensions imitate the functionality Haskell “do notation” </li></ul><ul><li>Monadic computations can hide a lot of implementation details from those using them. </li></ul><ul><ul><li>Failures using Option </li></ul></ul><ul><ul><li>State such as logging using the State monad. </li></ul></ul>
    22. 22. Discussion Can monads ever be “mainstream” ?
    23. 23. Links James Iry – “Monads are Elephants” http://james-iry.blogspot.com/2007/09/monads-are-elephants-part-1.html http://james-iry.blogspot.com/2007/10/monads-are-elephants-part-2.html http://james-iry.blogspot.com/2007/10/monads-are-elephants-part-3.html Philip Wadler’s Monad Papers http://homepages.inf.ed.ac.uk/wadler/topics/monads.html Brian Beckman Monad Videos http://channel9.msdn.com/shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads/ http://channel9.msdn.com/shows/Going+Deep/Brian-Beckman-The-Zen-of-Expressing-State-The-State-Monad/
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×