monads asking the right question
TRANSCRIPT
MonadsAsking the right question
WTF Monad?
In 30 minutes
WTF Monad?
“Hi”
“Sup?”
“I'm ready to write my first program. How can I print some stuff to console?”
“Use IO Monad”
“What the *** is a monad?”
WTF Monad?
1. We understand code
1. We understand code2. It is easier to talk about related ideas and
then abstract to something more general
1. We understand code2. It is easier to talk about related ideas and
then abstract to something more general 3. Having abstract model created, we tend to
create metaphors
1. We understand code2. It is easier to talk about related ideas and
then abstract to something more general 3. Having abstract model created, we tend to
create metaphors 4. Having all above we can formalize
Code & related ideas
Android, iOS, Windows Phone
Partner Lookup
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)case class WorkPlace(name: String, street: String)
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String =
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10")
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university)
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university)
penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny)
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university)
penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny)
println(partnerLookup(leonard)) println(partnerLookup(penny))
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university)
penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny)
println(partnerLookup(leonard)) // output: "Cake Street 1" println(partnerLookup(penny))
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university)
penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny)
println(partnerLookup(leonard)) // output: "Cake Street 1" println(partnerLookup(penny)) // output: "Academic St. 10"
“This app sucks!”
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace)case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val university = WorkPlace("University", "Academic St. 10")
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val university = WorkPlace("University", "Academic St. 10") val rajesh = Geek("Rajesh", workPlace = university)
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val university = WorkPlace("University", "Academic St. 10") val rajesh = Geek("Rajesh", workPlace = university)
println(partnerLookup(rajesh))
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val university = WorkPlace("University", "Academic St. 10") val rajesh = Geek("Rajesh", workPlace = university)
println(partnerLookup(rajesh))
// java.lang.NullPointerException
// at wtf.examples.A1$.partnerLookUp(A1.scala:14)
// at ...
case class Geek(name: String, partner: Geek = null, workPlace: WorkPlace) case class WorkPlace(name: String, street: String)
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
val university = WorkPlace("University", "Academic St. 10") val rajesh = Geek("Rajesh", workPlace = university)
println(partnerLookup(rajesh))
// java.lang.NullPointerException
// at wtf.examples.A1$.partnerLookUp(A1.scala:14)
// at ...
def partnerLookup(geek: Geek): String = {
}
def partnerLookup(geek: Geek): String = {
if(geek != null) { }
def partnerLookup(geek: Geek): String = {
if(geek != null) { if(geek.partner != null) { }
def partnerLookup(geek: Geek): String = {
if(geek != null) { if(geek.partner != null) { if(geek.partner.workPlace != null) {
}
def partnerLookup(geek: Geek): String = {
if(geek != null) { if(geek.partner != null) { if(geek.partner.workPlace != null) {
if(geek.partner.workPlace.street != null) {
}
def partnerLookup(geek: Geek): String = {
if(geek != null) { if(geek.partner != null) { if(geek.partner.workPlace != null) {
if(geek.partner.workPlace.street != null) { return geek.partner.workPlace.street
} } } }}
def partnerLookup(geek: Geek): String = {
if(geek != null) { if(geek.partner != null) { if(geek.partner.workPlace != null) {
if(geek.partner.workPlace.street != null) { return geek.partner.workPlace.street
} } } }
"not found"}
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university) val rajesh = Geek("Rajesh", workPlace = university)
penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny)
println(partnerLookup(leonard)) // output: "Cake Street 1" println(partnerLookup(penny)) // output: "Academic St. 10" println(partnerLookup(rajesh)) // output: "not found"
def partnerLookup(geek: Geek): String = {
if(geek != null) { if(geek.partner != null) { if(geek.partner.workPlace != null) {
if(geek.partner.workPlace.street != null) { return geek.partner.workPlace.street
} } } }
"not found"}
Call me: Maybe
sealed trait Maybe[+A]
sealed trait Maybe[+A] case class Some[+A](value: A) extends Maybe[A]
sealed trait Maybe[+A] case class Some[+A](value: A) extends Maybe[A] case object None extends Maybe[Nothing]
sealed trait Maybe[+A] case class Some[+A](value: A) extends Maybe[A] case object None extends Maybe[Nothing]
object Maybe { def apply[A](value: A) = Some(value)}
sealed trait Maybe[+A] {
}object Maybe { def apply[A](value: A) = Some(value)}
case class Some[+A](value: A) extends Maybe[A] {
}
case object None extends Maybe[Nothing] {
}
sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] }object Maybe { def apply[A](value: A) = Some(value)}
case class Some[+A](value: A) extends Maybe[A] {
}
case object None extends Maybe[Nothing] {
}
sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] }object Maybe { def apply[A](value: A) = Some(value)}
case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) }
case object None extends Maybe[Nothing] {
}
sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] }object Maybe { def apply[A](value: A) = Some(value)}
case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) }
case object None extends Maybe[Nothing] { def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this
}
case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace])case class WorkPlace(name: String, street: Maybe[String])
def partnerLookup(geek: Geek): String =
case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace])case class WorkPlace(name: String, street: Maybe[String])
def partnerLookup(geek: Geek): String = geek.partner
case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace])case class WorkPlace(name: String, street: Maybe[String])
def partnerLookup(geek: Geek): String = geek.partner.andThen(g => g.workPlace)
case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace])case class WorkPlace(name: String, street: Maybe[String])
def partnerLookup(geek: Geek): String = geek.partner.andThen(g => g.workPlace).andThen(wp => wp.street)
case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace])case class WorkPlace(name: String, street: Maybe[String])
def partnerLookup(geek: Geek): String = geek.partner.andThen(g => g.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street }
case class Geek(name: String, partner: Maybe[Geek] = wtf.monads.None, workPlace: Maybe[WorkPlace])case class WorkPlace(name: String, street: Maybe[String])
def partnerLookup(geek: Geek): String = geek.partner.andThen(g => g.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university) val rajesh = Geek("Rajesh", workPlace = university)
penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny)
println(partnerLookup(leonard)) println(partnerLookup(penny)) println(partnerLookup(rajesh))
val cheeseCakeFactory = WorkPlace("Cheese Cake Factory", "Cake Street 1") val university = WorkPlace("University", "Academic St. 10") var penny = Geek("Penny", workPlace = cheeseCakeFactory) var leonard = Geek("Leonard", workPlace = university) val rajesh = Geek("Rajesh", workPlace = university)
penny = penny.copy(partner = leonard) leonard = leonard.copy(partner = penny)
println(partnerLookup(leonard)) // output: "Cake Street 1" println(partnerLookup(penny)) // output: "Academic St. 10" println(partnerLookup(rajesh)) // output: "not found"
def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): String = geek.partner.workPlace.street
def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): String = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): String =
def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): Maybe[String] =
with a little bit of magic
For-comprehension
def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): Maybe[String] = {
}
def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): Maybe[String] = { import Magic._
}
def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(g => g.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] = geek.partner.andThen(p => p.workPlace).andThen(wp => wp.street) match { case wtf.monads.Some(street) => street case wtf.monads.None => "not found" }
def partnerLookup(geek: Geek): Maybe[String] = { import Magic._ for { p <- geek.partner wp <- p.workPlace s <- wp.street } yield (s)
}
var leonard = Geek("Leonard", workPlace = university, partner = penny)
partnerLookup(leonard) // Some("Cake Street 1") // how to print it?
sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B]
}object Maybe { def apply[A](value: A) = Some(value)}
case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value)
}
case object None extends Maybe[Nothing] { def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this
}
sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] def within[B](f: A => B): Maybe[B]}object Maybe { def apply[A](value: A) = Some(value)}
case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) }
case object None extends Maybe[Nothing] { def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this }
sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] def within[B](f: A => B): Maybe[B]}object Maybe { def apply[A](value: A) = Some(value)}
case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) }
case object None extends Maybe[Nothing] { def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this }
Reuse existing functionality like println
sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] def within[B](f: A => B): Maybe[B]}object Maybe { def apply[A](value: A) = Some(value)}
case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) def within[B](f: A => B): Maybe[B] = Maybe(f(value))}
case object None extends Maybe[Nothing] { def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this }
sealed trait Maybe[+A] { def andThen[B](f: A => Maybe[B]): Maybe[B] def within[B](f: A => B): Maybe[B]}object Maybe { def apply[A](value: A) = Some(value)}
case class Some[+A](value: A) extends Maybe[A] { def andThen[B](f: A => Maybe[B]): Maybe[B] = f(value) def within[B](f: A => B): Maybe[B] = Maybe(f(value))}
case object None extends Maybe[Nothing] { def andThen[B](f: Nothing => Maybe[B]): Maybe[B] = this def within[B](f: Nothing => B): Maybe[B] = this}
var leonard = Geek("Leonard", workPlace = university, partner = penny)
partnerLookup(leonard) // Some("Cake Street 1") // how to print it?
var leonard = Geek("Leonard", workPlace = university, partner = penny)
partnerLookup(leonard)
var leonard = Geek("Leonard", workPlace = university, partner = penny)
partnerLookup(leonard).within(println) // output: "Cake Street 1"
Android, iOS, Windows Phone
Ship Inventory
case class Pirate(name: String, ships: Many[Ship])
case class Ship(name: String, hold: Hold)
case class Hold(barrel: Many[Barrel])
case class Barrel(amount: Int)
case class Pirate(name: String, ships: Many[Ship])
case class Ship(name: String, hold: Hold)
case class Hold(barrel: Many[Barrel])
case class Barrel(amount: Int)
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
case class Pirate(name: String, ships: Many[Ship])
case class Ship(name: String, hold: Hold)
case class Hold(barrel: Many[Barrel])
case class Barrel(amount: Int)
val blackPearl = Ship("Black Pearl", blackHold)
val whitePearl = Ship("White Pearl", whiteHold)
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
case class Pirate(name: String, ships: Many[Ship])
case class Ship(name: String, hold: Hold)
case class Hold(barrel: Many[Barrel])
case class Barrel(amount: Int)
val blackHold = Hold(Empty)
val whiteHold = Hold(Many(Barrel(20), Barrel(10)))
val blackPearl = Ship("Black Pearl", blackHold)
val whitePearl = Ship("White Pearl", whiteHold)
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
sealed trait Many[+A]
sealed trait Many[+A]
case class Const[+A](head: A, tail: Many[A]) extends Many[A]
sealed trait Many[+A]
case class Const[+A](head: A, tail: Many[A]) extends Many[A]
case object Empty extends Many[Nothing]
sealed trait Many[+A]
case class Const[+A](head: A, tail: Many[A]) extends Many[A]
case object Empty extends Many[Nothing]
object Many {
def apply[A](elements: A*): Many[A] = {
if(elements.length == 1) Const(elements(0), Empty)
else Const(elements(0), apply(elements.drop(1) : _*))
}
}
sealed trait Many[+A] {
def andThen[B](f: A => Many[B]): Many[B]
def within[B](f: A => B): Many[B]
}
case class Const[+A](head: A, tail: Many[A]) extends Many[A] {
def andThen[B](f: A => Many[B]): Many[B] = ...
def within[B](f: A => B): Many[B] = ...
}
case class Const[+A](head: A, tail: Many[A]) extends Many[A] {
def andThen[B](f: A => Many[B]): Many[B] = ...
def within[B](f: A => B): Many[B] = Const(f(head), tail.within(f))
}
case class Const[+A](head: A, tail: Many[A]) extends Many[A] {
def andThen[B](f: A => Many[B]): Many[B] =
concat( f(head), tail.andThen(f) )
def within[B](f: A => B): Many[B] = Const(f(head), tail.within(f))
}
case class Const[+A](head: A, tail: Many[A]) extends Many[A] {
def andThen[B](f: A => Many[B]): Many[B] =
concat( f(head), tail.andThen(f) )
private def concat[A](first: Many[A],
second: Many[A]): Many[A] = { … }
def within[B](f: A => B): Many[B] = Const(f(head), tail.within(f))
}
case class Const[+A](head: A, tail: Many[A]) extends Many[A] {
def andThen[B](f: A => Many[B]): Many[B] =
concat( f(head), tail.andThen(f) )
private def concat[A](first: Many[A],
second: Many[A]): Many[A] = { … }
def within[B](f: A => B): Many[B] = Const(f(head), tail.within(f))
}
case object Empty extends Many[Nothing] {
def andThen[B](f: Nothing => Many[B]): Many[B] = this
def within[B](f: Nothing => B): Many[B] = this
}
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
val blackPearl = Ship("Black Pearl", Hold(Empty))
val whitePearl = Ship("White Pearl",
Hold(Many(Barrel(20), Barrel(10))))
val jack = Pirate("Captain Jack Sparrow", Many(blackPearl, whitePearl))
jack.ships.andThen(ship => ship.hold.barrel)
.within(barrel => barrel.amount)
import Magic._
val amounts = for {
ship <- jack.ships
barrel <- ship.hold.barrel
} yield (barrel.amount)
println(amounts) // Const(20,Const(10,Empty))
● We've seen two wrapping classes.
● We've seen two wrapping classes. ● All have andThen method.
● We've seen two wrapping classes. ● All have andThen method.● Each can take a function that can be called
on a value wrapped by the structure (which can be empty or there can be multiple instances of it).
Both were examples of a Monad!
WTF Monad?
Monad is a container, a box.
Monad is a container, a box.A box in which we can store an object. Wrap it.
Monad is a container, a box.A box in which we can store an object, a value.A box that has a common interface, which allows us to do one thing: connect sequence of operations on the content of the box.
Monad is a container, a box.A box in which we can store an object, a value.A box that has a common interface, which allows us to do one thing: connect sequence of operations on the content of the box.andThen means "do the next things".
Monad is a container, a box.A box in which we can store an object, a value.A box that has a common interface, which allows us to do one thing: connect sequence of operations on the content of the box.andThen means "do the next things". But the way it is done depends on a box.
Monad is a container, a box.A box in which we can store an object, a value.A box that has a common interface, which allows us to do one thing: connect sequence of operations on the content of the box.andThen means "do the next things". But the way it is done depends on a box.
Monad is a container, a box.A box in which we can store an object, a value.A box that has a common interface, which allows us to do one thing: connect sequence of operations on the content of the box.andThen means "do the next things". But the way it is done depends on a box.
Monad is a container, a box.A box in which we can store an object, a value.A box that has a common interface, which allows us to do one thing: connect sequence of operations on the content of the box.andThen means "do the next things". But the way it is done depends on a box.
Monad is an abstract data type.
Monad is an abstract data type.It has two operators: andThen and unit
Monad is an abstract data type.It has two operators: andThen and unit
object Maybe { def apply[A](value: A) = Some(value)
}
Monad is an abstract data type.It has two operators: andThen and unit
Monad is an abstract data type.It has two operators: andThen and unit
Monad is an abstract data type.It has two operators: andThen and unit bind (>>=) // formal name
Monad is an abstract data type.It has two operators: andThen and unit bind (>>=) // formal name flatMap // in Scala
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
object Magic {
case class RichMaybe[A](m: Maybe[A]) {
def flatMap[B](f: A => Maybe[B]): Maybe[B] = m.andThen(f)
def map[B](f: A => B): Maybe[B] = m.within(f)
}
implicit def enrich[A](m: Maybe[A]) = RichMaybe(m)
}
def partnerLookup(geek: Geek): Maybe[String] = {
import Magic._
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
def partnerLookup(geek: Geek): Maybe[String] = {
for {
p <- geek.partner
wp <- p.workPlace
s <- wp.street
} yield (s)
}
Monad is an abstract data type.It has two operators: bind and unit
Monad is an abstract data type.It has two operators: bind and unitThose operators must follow certain rules:1. associativity2. left identity3. right identity
1. associativity2. left identity3. right identity
unit acts approximately as a neutral element of bind
1. associativity2. left identity3. right identity
unit acts approximately as a neutral element of bind
unit allows us to put value into the box without damaging it
1. associativity2. left identity3. right identity
unit acts approximately as a neutral element of bind
unit(x).flatMap(f) == f(x)m.flatMap(unit) == m
unit allows us to put value into the box without damaging it
1. associativity2. left identity3. right identity
“Binding two functions in succession is the same as binding one function that can be determined from them”
1. associativity2. left identity3. right identity
m.flatMap{f(_)}.flatMap.{g(_)} ==m.flatMap{f(_).flatMap.{g(_)}}
“Binding two functions in succession is the same as binding one function that can be determined from them”
WTF Monad?
Paweł Szulc
Paweł Szulchttp://rabbitonweb.com
@rabbitonwebhttps://github.
com/rabbitonweb/monads_asking_the_right_question
Thank you!
Images usedhttp://beforeitsnews.com/business/2014/02/career-tips-how-to-look-presentable-for-a-job-interview-2591264.html
http://phantommagician.deviantart.com/art/Cursed-Gold-183261313
http://carlosandgabbysbrooklyn.com/Burritos.html
http://channel9.msdn.com/Forums/Coffeehouse/442850-Sign-up-for-American-Programmer-C9park
https://www.pinterest.com/sandinarvaez/cute-graphics-and-patterns/
http://dsmconsulting.com.au/whats-in-the-box/
http://www.concordmonitor.com/home/4293575-95/cat-cats-videos-henri
https://www.pinterest.com/JoanneBest3/cats-in-boxes/
http://essaylab.org/
http://www.fanpop.com/clubs/pirates-of-the-caribbean/
http://library.sun.ac.za/SiteCollectionImages/search/Launch-Button.jpg