Download - The Essence of the Iterator Pattern (pdf)
Computation Functor
APPLICATIVE For loop Traverse
Computation
K[T]
A type of
computation
A type of value
Computations
Option[T] Zero or one
List[T] Zero or more
Future[T] Later
State[S, T] Depend on S
IO[T] Ext. effects
Create computations?
Option[T] Some(t)
List[T] List(t)
Future[T] future(t)
State[S, T] state(s => (s, t))
IO[T] IO(t)
Pointed
K[T].point(t)
Compute a value
Use computations?
Option[T] Some(2)
List[T] List(1, 2)
Future[T] future(calculate)
State[S, T] state(s => (s, s+1))
IO[T] IO(println(“hello”))
Functor
K[T] map f
Use the value
Functors map
Option[T] modify the value
List[T] modify the values
Future[T] modify later
State[S, T] modify ts
IO[T] modify the action
Applicative
getUser(props: Properties): String
getConnection(user: String, pw: String):
Connection = {
if (user != null && pw != null)
....
}
Before
getPassword(props: Properties): String
getConnection(getUser(p), getPassword(p))
Applicative
getUser(props: Properties): Option[String]
getConnection(user: String, pw: String):
Connection = {
if (user != null && pw != null)
....
}
After
getPassword(props: Properties): Option[String]
getConnection(?, ?)
Applicative
f(a, b)
f(K[a], K[b])
How?
Use Pointed
f(a:A, b:B): C
fk: K[A => B => C]
point
Applicative
K[A => B] <*> K[A]
Apply the function
==
K[B]
Applicative
K[A => B => C] <*>
K[A] <*> K[B]
Currying ftw!
==
K[B => C] <*> K[B]
==
K[C]
Applicative
K(f) <*> K(a) <*> K(b)
Apply ‘f’ to ‘a’ and ‘b’ “inside” ‘K’
Applicative
K(f) <*> K(a) <*> K(b)
Apply ‘f’ to ‘a’ and ‘b’ “inside” ‘K’
Applicative
Option
Some(getConnection.curried) <*>
user(p) <*>
password(p)
Applicative
Option
(user(p) <**> password(p))(mkConnection)
mkConnection <$> user(p) <*> password(p)
Applicative
Future
future(discount(_,_))) <*>
future(amount) <*>
future(rate)
: Future[Double]
Applicative
List
List(plus1) <*> List(1, 2, 3)
List(2, 3, 4)
Applicative
List
List(plus1, plus2) <*> List(1, 2, 3)
ratings <*> clients
== List(2, 3, 4, 3, 4, 5)
Applicative
ZipList
List(plus1, plus2, plus3) <*>
List(1, 2, 3)
== List(1, 4, 6)
Applicative State
val add = (i: Int) => (j: Int) => i+j
val times = (i: Int) => (j: Int) => i*j
// 0 -> 1, 2, 3, 4
val s1 = modify((i: Int) => i+1)
(add <$> s1 <*> s1)(1) == ?
(times <$> s1 <*> s1)(1) == ?
Applicative State
(add <$> s1 <*> s1)(1) == (3, 5)
multiply 2 previous states
+1=2 +1=3
current state
add 2 previous states
+1=2 +1=3
(times <$> s1 <*> s1)(1) == (3, 6)
Monad, remember?
def unit[A](a: =>A): M[A]
def bind[A, B](ma: M[A])(f: A => M[B]): M[B]
Unit
Bind
Monad => Applicative
def point(a: =>A) = Monad[M].unit(a)
Point
def <*>[A, B](mf: M[A=>B])(ma: M[A]):M[B] =
Monad[M].bind(mf) { f =>
Monad[M].bind(ma) { a =>
f(a) // M[B]
} // M[B]
} // M[B]
Apply
The “for” loop val basket = Basket(orange, apple)
var count = 0
val juices = Basket[Juice]()
for (fruit <- basket) {
count = count + 1
juices.add(fruit.press)
}
same container for the result
“mapping”
accumulation
Traverse
def traverse(f: A => F[B]): T[A] => F[T[B]]
Traversable
Applicative Same structure
Traverse a List
List(x, y, z): List[A]
f: A => F[B]
Traverse a List
F(::) <*> F(z) <*> F(Nil)
Apply ‘f’ to ‘z’
“Rebuild” the list
F(z :: Nil)
Traverse a List
F(::) <*> F(y) <*> F(z :: Nil)
F(y :: z :: Nil)
F(::) <*> F(x) <*> F(y::z::Nil)
F(x :: y :: z :: Nil)
Traverse a
Binary Tree
x
f
z y
x
y z z y x
z y
`sequence`
def sequence[F: Applicative]:
T[F[A]] => F[T[A]] =
traverse(identity)
`sequence`
val executing: Seq[Promise[Result]] =
examples.map(e => promise(e.execute))
val results: Promise[Seq[Result]] =
executing.sequence
val examples: Seq[Example] =
Seq(e1, e2, e3)
Promise of a sequence
Sequence of promises
Execute concurrently?
Measure with
Monoids
def measure[T: Traversable, M : Monoid]
(f: A => M): T[A] => M
Count elements: Int Monoid
Accumulate elements: List Monoid
trait Monoid[A] {
val zero: A; def append(a: A, b: A): A
}
`measure`
def measure[T: Traversable, M : Monoid]
(f: A => M) =
traverse(a => f(a))
`Const`
case class Const[M, +A](value: M)
“Phantom “ type
new Applicative[Const[M, *]] {
def point(a: =>A) = Const(Monoid[M].zero)
def <*>(f: Const[M, A=>B], a: Const[M, A]) =
Const(Monoid[M].append(f.value, a.value))
}
Applicative => Monad
def unit[A](a: =>A) = Const(Monoid[M].zero)
Unit
def bind[A, B](ma: Const[M, A])
(f: A => Const[M, B]) =
=> but no value `a: A` to be found!
Bind
`measure`
def sumSizes[A : Size](seq: Seq[A]) =
measure(a => Size[A].size(a))(seq)
Sum up all sizes
def collectSizes[A : Size](seq: Seq[A]) =
measure(a => List(Size[A].size(a)))(seq)
Collect all sizes
`contents`
def contents[A](tree: Tree[A]): List[A] =
measure(a => List(a))(tree)
x
z y
=> List(x, y, z)
`shape`
def shape[A](tree: Tree[A]): Tree[Unit] =
map(a => ())(tree)
x
z y
=> .
. .
def map[A, B](f: A => B): T[A] =>
traverse(a => Ident(f(a)))
Identity monad
`decompose`
def decompose[A](tree: Tree[A]) =
(contents(tree), shape(tree))
Not very efficient…
x
z y
=> .
. .
List(x, y, z)
Applicative products
case class Product[F1[_], F2[_], A](
first: F1[A], second: F2[A])
F1: Applicative, F2: Applicative def point[A, B](a: => A) =
Product[F1, F2, B](Pointed[F1].point(a),
Pointed[F2].point(a))
def <*>[A, B](f: Product[F1, F2, A => B]) =
(c: Product[F1, F2, A]) =>
Product[F1, F2, B](f.first <*> c.first,
f.second <*> c.second)
`contents ⊗ shape`
F1 = Const[List[A], *] F2 = Ident[*]
val contents = (a: A) => Const[List[A], Unit](List(a))
val shape = (a: A) => Ident(())
val contentsAndShape:
A => Product[Const[List[A], _], Ident[_], *] =
contents ⊗ shape
tree.traverse(contentsAndShape)
Type indifference
List[Int]: Monoid Applicative => Const[List[Int], _]
({type l[a]=Const[List[Int], a]})#l
One parameter type constructor trait Apply[F[_]] {
def <*>[A, B](f: F[A => B]): F[A] => F[B]
}
Type indifference
({type l[a]=Const[List[Int], a]})#l
type ApplicativeProduct =
({type l[a]=Product[Const[List[A],_],Ident[_],a]})#l
Anonymous type
({type l[a]=Const[List[Int], a]})#l
Type member
({type l[a]=Const[List[Int], a]})#l
Type projection
Type indifference
Measure def measure[M : Monoid](f: T => M): M =
traverse(t => Monoid[M].unit(f(t))).value
def measure[M : Monoid](f: A => M): M =
traverse[(type l[a]=Const[M, a]})#l, A, Any] { t =>
Monoid[M].point(f(t))
}.value
For real…
`collect`
Accumulate and map def collect[F[_] : Applicative, A, B]
(f: A => F[Unit], g: A => B) = {
traverse { a: A =>
Applicative[F].point((u: Unit) => g(a)) <*> f(a))
}
}
val count = (i: Int) => state((n: Int) => (n+1, ()))
val map = (i: Int) => i.toString
tree.collect(count, map).apply(0)
(2, Bin(Leaf("1"), Leaf("2")))
`disperse`
Label and map def disperse(F[_] : Applicative, A, B, C]
(f: F[B], g: A => B => C): T[A] => F[T[C]]
val tree = Bin(Leaf(1.1), Bin(Leaf(2.2), Leaf(3.3)))
val label = modify((n:Int) => n+1)
val name = (p1:Double) => (p2:Int) => p1+" node is "+p2
tree.disperse(label, name).apply(0)._2
Bin(Leaf("1.1 node is 1"),
Bin(Leaf("2.2 node is 2"), Leaf("3.3 node is 3")))
val crosses = modify((s: String) => s+"x")
val map = (i: Int) => i.toString
tree.measure(crosses, map).apply("")
("xxx", Bin(Leaf("1"), Bin(Leaf("2"),
Leaf("3"))))
EIP `measure`
def measure[F[_] : Applicative, A, B]
(f: F[B], g: A => C): T[A] => F[C]
Map and count
Traversals
function map element create state mapped depend
on state
state depend on
element
collect X X X
disperse X X X
measure X X
traverse X X X X
reduce X X
reduceConst X
map X
Quizz
def findMatches(divs: Seq[Int], nums: Seq[Int])
findMatches(Seq(2, 3, 4), Seq(1, 6, 7, 8, 9))
=> Seq((2, 6), (3, 9), (4, 8))
With Traverse?
Quizz
def findMatches(divs: Seq[Int], nums: Seq[Int]) = {
case class S(matches: Seq[(Int, Int)] = Seq[(Int, Int)](),
remaining: Seq[Int])
val initialState = S(remaining = nums)
def find(div: Int) = modify { (s: S) =>
s.remaining.find(_ % div == 0).map { (n: Int) =>
S(s.matches :+ div -> n, s.remaining - n)
}.getOrElse(s)
}
divs.traverse(find).exec(initialState).matches
}
Composition
val results = new ListBuffer
for (a <- as) {
val currentSize = a.size
total += currentSize
results.add(total)
}
F1 (map) then F2 (sum)
F2 [F1[_]] => Applicative?
`assemble`
def assemble[F[_] : Applicative, A]:
(f: F[Unit], g: List[A]): T[A] => F[A]
Shape + content => assembled
val shape: BinaryTree[Unit] = Bin(Leaf(()), Leaf(()))
shape.assemble(List(1, 2))
(List(), Some(Bin(Leaf(1), Leaf(2))))
shape.assemble(List(1, 2, 3))
(List(3), Some(Bin(Leaf(1), Leaf(2))))
shape.assemble(List(1))
(List(), None)
def takeHead: State[List[B], Option[B]] =
state { s: List[B] =>
s match {
case Nil => (Nil, None)
case x :: xs => (xs, Some(x))
}
}
`assemble`
F1: Option[_] An element to insert
F2 :State[List[A], _] the rest of the list
F2 [F1]: State[List[A], Option[_]] An applicative
`assemble`
def assemble[F[_] : Applicative, A]
(f: F[Unit], list: List[A]) =
traverse(takeHead).apply(list)
Monadic composition
val f: B => M[C]
val g: A => M[B]
val h: A => M[C] = f • g
M : Monad
Fusion?
traverse(f) • traverse(g) == traverse(f • g)
Monadic composition
val xy = for {
x <- (mx: M[X])
y <- (my: M[Y])
} yield (x, y)
Yes if the Monad is commutative
State is *not* commutative
val yx = for {
y <- (my: M[Y])
x <- (mx: M[X])
} yield (x, y)
xy == yx
val mx = state((n: Int) => (n+1, n+1))
val my = state((n: Int) => (n+1, n+1))
xy.apply(0) == (2, (1, 2))
yx.apply(0) == (2, (2, 1))
Applicative composition vs
Monadic composition
Not commutative functions => fusion
Seq(1,2,3).traverse(times2 ⊙ plus1) == 4
State[Int, State[Int, Seq[Int]]]
Seq(1,2,3).traverse(times2) ⊙
Seq(1,2,3).traverse(plus1) == 4
State[Int, Seq[State[Int, Int]]
Monadic composition:
conjecture
Commutative functions val plus1 = (a: A) => state((n: Int) => (n+1, a))
val plus2 = (a: A) => state((n: Int) => (n+2, a))
val times2 = (a: A) => state((n: Int) => (n*2, a))
plus1 and plus2 are commutative
plus1 and times2 are not commutative:
(0 + 1) * 2 != (0 * 2) + 1
Commutative functions => fusion
Seq(1,2,3).traverse(plus2 ∎ plus1) == 10 Seq(1,2,3).traverse(plus2) ∎ Seq(1,2,3).traverse(plus1) == 10
Not commutative functions => no fusion Seq(1,2,3).traverse(times2 ∎ plus1) == 22 Seq(1,2,3).traverse(times2) ∎ Seq(1,2,3).traverse(plus1) == 32
Monadic composition:
conjecture