Monads And why they are so useful flat

  • Slides: 30
Download presentation
Monads And why they are so useful

Monads And why they are so useful

flat. Map revisited n The flat. Map method is like Map, but removes one

flat. Map revisited n The flat. Map method is like Map, but removes one level of nesting from a sequence n n n scala> List(2, 3, 4, 5) map (x => List(x, x * x * x)) res 21: List[Int]] = List(2, 4, 8), List(3, 9, 27), List(4, 16, 64), List(5, 25, 125)) scala> List(2, 3, 4, 5) flat. Map (x => List(x, x * x * x)) res 22: List[Int] = List(2, 4, 8, 3, 9, 27, 4, 16, 64, 5, 25, 125) Used with a sequence of Option, flat. Map effectively reduces Some(x) to x, and entirely deletes None n scala> List(1, -1, 2, 4, -5, 9) flat. Map (root(_)) res 17: List[Double] = List(1. 0, 1. 4142135623730951, 2. 0, 3. 0) 2

map and for…yield n n n val nums = List(1, 2, 3) nums map

map and for…yield n n n val nums = List(1, 2, 3) nums map (_ * 2) List(2, 4, 6) for (n <- nums) yield (n * 2) List(2, 4, 6) nums map (n => List(n, 2 * n, 3 * n)) List(1, 2, 3), List(2, 4, 6), List(3, 6, 9)) nums map (n => List(n, 2 * n, 3 * n)) flatten List(1, 2, 3, 2, 4, 6, 3, 6, 9) nums flat. Map (n => List(n, 2 * n, 3 * n)) List(1, 2, 3, 2, 4, 6, 3, 6, 9) 3

More map and for…yield n n n for (x <- List(1, 2, 3); y

More map and for…yield n n n for (x <- List(1, 2, 3); y <- List(10, 100)) yield x * y List(10, 100, 200, 300) List(1, 2, 3) flat. Map { x => List(10, 100) map (y => x * y) } List(10, 100, 200, 300) for (x <- List(1, 2, 3, 4) if x != 3) yield 2 * x List(2, 4, 8) List(1, 2, 3, 4) filter (_ != 3) map (2 * _) List(2, 4, 8) for…yield is not a loop—it is syntactic sugar for a sequence of map, flatmap, and filter operations n Of course, those operations may be implemented using loops for without a yield is equivalent to a foreach 4

Functors n n In Scala terms, a functor is anything that has a map

Functors n n In Scala terms, a functor is anything that has a map operation Scala’s collections (lists, arrays, sets, maps, strings, etc. ) have a map operation n n It is implemented differently for different collection types trait F[T] { def map(f: T => S): F[S] } 5

The Option type n n Scala has null because it interoperates with Java; it

The Option type n n Scala has null because it interoperates with Java; it shouldn’t be used any other time Instead, use an Option type, with values Some(value) and None n n def max(list: List[Int]) = { if (list. length > 0) { val big = list reduce {(a, b) => if (a > b) a else b} Some(big) } else { None } max(my. List) match { case Some(x) => println("The largest number is " + x) case None => println("There are no numbers here!!!") }

Try n scala> import scala. util. {Try, Success, Failure} scala> def root 2(x: Double):

Try n scala> import scala. util. {Try, Success, Failure} scala> def root 2(x: Double): Try[Double] = | if (x >= 0) Success(math. sqrt(x)) else | Failure(new Exception("Imaginary root")) root 2: (x: Double)scala. util. Try[Double] n n scala> root 2(10) res 28: scala. util. Try[Double] = Success(3. 1622776601683795) scala> root 2(-10) res 29: scala. util. Try[Double] = Failure(java. lang. Exception: Imaginary root) 7

Containers I n n n The map operation is also useful for various types

Containers I n n n The map operation is also useful for various types of containers Some(5) map (x => 2 * x) Some(10) val nope: Option[Int] = None import scala. util. {Try, Success, Failure} Success(5) map (x => 2 * x) Success(10) val fail: Try[Int] = Failure(new Exception) fail map (x => 2 * x) Failure(java. lang. Exception) 8

Containers II n n n import scala. concurrent. _ import scala. concurrent. duration. _

Containers II n n n import scala. concurrent. _ import scala. concurrent. duration. _ import scala. concurrent. Execution. Context. Implicits. global val foo = Future{42} map (x => 2 * x) List() Await. result(foo, 1 nano) 84 Therefore, Option (Some, None), Try (Success, Failure), and Future also functors 9

Monad as a trait n In Scala, we could define a monad trait as

Monad as a trait n In Scala, we could define a monad trait as follows: trait M[A] { def flat. Map[B](f: A => M[B]): M[B] } def unit[A](x: A): M[A] n You can think of unit as a constructor, or better yet, as a factory method n n There isn’t actually any unit method in Scala, but there is apply Notice that monads have a type parameter (A) If a Scala object has a constructor with a type parameter and a flat. Map operation, it’s a monad! Scala is full of monads n Examples: List, Set, Map, Array, String, Option, Try, Future, to name a few 10

Comparing map and flat. Map n val list. A: List[A] = … n final

Comparing map and flat. Map n val list. A: List[A] = … n final def flat. Map[B](f: (A) ⇒ M[B]): List[B] map[B](f: (A) ⇒ B): List[B] where M = Gen. Traversable. Once n n n val nums = List(1, 100) x map (v => List(v - 1, v, v + 1)) res 0: List[Int]] = List(0, 1, 2), List(9, 10, 11), List(99, 100, 101)) x flat. Map (v => List(v - 1, v, v + 1)) res 1: List[Int] = List(0, 1, 2, 9, 10, 11, 99, 100, 101) 11

Monad in Haskell and Scala n Remember Haskell? n A monad consists of three

Monad in Haskell and Scala n Remember Haskell? n A monad consists of three things: n n A type constructor M A bind operation, (>>=) : : (Monad m) => m a -> (a -> m b) -> m b A return operation, return : : (Monad m) => a -> m a Compare: n n A Haskell type constructor with a Scala type declaration A Haskell bind operation with Scala’s flat. Map n n n Haskell: m a -> (a -> m b) -> m b Scala: M(A). (f: A => M[B]): M[B] A Haskell return operation with a Scala constructor (or unit) 12

and. Then does sequencing n scala> def double(x: Int) = 2 * x double:

and. Then does sequencing n scala> def double(x: Int) = 2 * x double: (x: Int)Int scala> def triple(x: Int) = 3 * x triple: (x: Int)Int scala> ((double _) and. Then (triple _))(5) res 4: Int = 30 n scala> def upper(s: String) = s. to. Upper. Case upper: (s: String)String scala> def add. Xs(s: String) = "x" + s + "x" add. Xs: (s: String)String scala> ((upper _) and. Then (add. Xs _))("Hello") res 10: String = x. HELLOx 13

foo 1 n Method to print a string, then return its length: n n

foo 1 n Method to print a string, then return its length: n n scala> def foo 1(bar: String) = { | println(bar) | bar. size | } foo 1: (bar: String)Int scala> foo 1("Hello") Hello res 0: Int = 5 14

foo 2 n Here’s the same method, but replacing each expression with an anonymous

foo 2 n Here’s the same method, but replacing each expression with an anonymous function: n n scala> def foo 1(bar: String) = { | (() => println(bar))() | (() => bar. length)() | } foo 1: (bar: String)Int scala> foo 2("Hello") Hello res 1: Int = 5 15

and. Then applied to foo n Consider this form: n n def foo(bar: String)

and. Then applied to foo n Consider this form: n n def foo(bar: String) = { ({ () => println(bar) } and. Then { () => bar. length })() } The above almost works… n n n is not defined for 0 -argument functions Basically, what this achieves is sequencing in a purely functional manner In pure functions, there is no concept of sequencing and. Then 16

Thing n Compare: n def foo(i: Int) = i + 1 val a =

Thing n Compare: n def foo(i: Int) = i + 1 val a = 1 val b = foo(a) n With: n case class Thing[+A](value: A) val a = Thing(1) val b = Thing(2) def foo(i: Int) = Thing(i + 1) val a = Thing(1) val b = foo(a. value) n The difference is that in the second, the value is “wrapped” in a Thing container 17

Monads as wrappers n A monad consists of three things: n n A type

Monads as wrappers n A monad consists of three things: n n A type constructor M A bind operation, (>>=) : : (Monad m) => m a -> (a -> m b) -> m b A return operation, return : : (Monad m) => a -> m a Is Thing a monad? n n n It has a type constructor, Thing It has a return operation, Thing(i) Let’s give it a bind operation: case class Thing[+A](value: A) { def bind[B](f: A => Thing[B]) = f(value) } 18

The Thing monad n Here’s what we had before: n scala> val a =

The Thing monad n Here’s what we had before: n scala> val a = Thing(1) a: Thing[Int] = Thing(1) scala> val b = foo(a. value) b: Thing[Int] = Thing(2) n Here’s what we have now: n scala> val a = Thing(1) a: Thing[Int] = Thing(1) scala> val b = a bind foo b: Thing[Int] = Thing(2) n We have additional syntax, but really, nothing’s changed 19

The monad pattern n n Any time you start with something which you pull

The monad pattern n n Any time you start with something which you pull apart and use to compute a new something of that same type, you have a monad. val a = Thing(1) n n The first thing is that I can wrap up a value inside of a new Thing. Objectoriented developers might call this a “constructor”. Monads call it “the unit function”. Haskell calls it “return” (maybe we shouldn’t try to figure out that one). a bind { i => Thing(i + 1) } n We also have this fancy bind function, which digs inside our Thing and allows a function which we supply to use that value to create a new Thing. Scala calls this function “flat. Map”. Haskell calls it “>>=”. …What’s interesting here is the fact that bind is how you combine two things together in sequence. Directly quoted from www. codecommit. com/blog/ 20

bind == flat. Map n n Scala’s for expression is translated into map, flat.

bind == flat. Map n n Scala’s for expression is translated into map, flat. Map, and with. Filter operations Multiple generators lead to a flat. Map n n for (x <- expr 1; y <- expr 2; seq) yield expr 3 gets translated to expr 1. flat. Map(x => for (y <- expr 2; seq) yield expr 3) Repeated use of flat. Map will change List[List[items]]] into just List[items] 21

Using flat. Map n n scala> for (v <- List(1, 2, 3, -1, 4))

Using flat. Map n n scala> for (v <- List(1, 2, 3, -1, 4)) { | val Some(root. Of. V) = root(v) | println(root. Of. V) | } 1. 0 1. 4142135623730951 1. 7320508075688772 scala. Match. Error: None (of class scala. None$) scala> for (v <- List(1, 2, 3, -1, 4) flat. Map (root(_))) println(v) 1. 0 1. 4142135623730951 1. 7320508075688772 2. 0 22

Option n A value of type Option[T] can be either Some[value] or None, where

Option n A value of type Option[T] can be either Some[value] or None, where value is of type T n n n scala> def root(x: Double): Option[Double] = | if (x >= 0) Some(math. sqrt(x)) else None root: (x: Double)Option[Double] scala> root(10. 0) res 14: Option[Double] = Some(3. 1622776601683795) scala> root(-5. 0) res 15: Option[Double] = None 23

Repeated flat. Map n n Let’s say we want to load a user from

Repeated flat. Map n n Let’s say we want to load a user from the database and if he exists we want to see if he has a grandchild. We need to invoke these three functions: String → Option[User] // load from db User → Option[User] // get child’s child Here’s the code. val result = User. Service. load. User("mike"). flat. Map(get. Child) Or we can do this: val result = for { user <- User. Service. load. User("mike) users. Child <- user. child users. Grand. Child <- users. Child. child } yield users. Grand. Child Example from: https: //medium. com/@sinisalouc/demystifying-the-monad-in-scalacc 716 bb 6 f 534#. jf 1 x 02 xm 9 24

bind for Option n sealed trait Option[+A] { def bind[B](f: A => Option[B]): Option[B]

bind for Option n sealed trait Option[+A] { def bind[B](f: A => Option[B]): Option[B] } case class Some[+A](value: A) extends Option[A] { def bind[B](f: A => Option[B]) = f(value) } case object None extends Option[Nothing] { def bind[B](f: Nothing => Option[B]) = None } 25

Maintaining state n A purely functional language has no notion of “state” (or time,

Maintaining state n A purely functional language has no notion of “state” (or time, or change…) n n n Everything relevant to a function is in its parameters Therefore, a function that “changes state” must be called recursively with different parameters Consider an adventure game n n n State includes the location of each object and the location of the player—this is easily done with a Map The state usually includes other information (is the dragon alive? )—we can put this in a tuple along with the Map Player’s actions can be implemented with a function that takes a State and computes a new State—that is, a monad 26

Life, the Universe, and Everything n Passing around the entire “state of the universe”

Life, the Universe, and Everything n Passing around the entire “state of the universe” in parameters seems excessive, but… n n n Typically a very large proportion of the information is immutable, and need not be part of the state You have to depend on the quality of the implementation of persistent data structures Scala has a specific State monad n I haven’t explored this, but I’ve read that it’s complicated 27

A “functional” println n def foo(bar: String, stdout: Vector[String]) = { val stdout 2

A “functional” println n def foo(bar: String, stdout: Vector[String]) = { val stdout 2 = println(bar, stdout) (bar. length, stdout 2) } def println(str: String, stdout: Vector[String]) = stdout + str n n n Now we can use and. Then Functional input is trickier—we won’t go there Haskell does this for everything! 28

And then there’s the IO monad… n n Haskell’s IO monad is like our

And then there’s the IO monad… n n Haskell’s IO monad is like our earlier “functional” println, only richer and with a better syntax Like all monads, it pulls apart some kind of thing, and creates a new thing from it n n n The weird part is, I/O happens along the way Output doesn’t affect the result Input does affect the result The IO monad (1) achieves sequencing, and (2) isolates the I/O side effects from the rest of the program In Scala we could use a functional println to implement (part of) an IO monad—but why bother? 29

The End n Substantial portions of this talk taken from: http: //www. codecommit. com/blog/

The End n Substantial portions of this talk taken from: http: //www. codecommit. com/blog/ 30