The Essence of the Iterator Pattern
-
Upload
eric-torreborre -
Category
Technology
-
view
1.702 -
download
2
description
Transcript of The Essence of the Iterator Pattern
Computation
Functor
APPLICATIVE For loopTraverse
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) ....}
BeforegetPassword(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’
ApplicativeOption
Some(getConnection.curried) <*> user(p) <*> password(p)
ApplicativeOption
(user(p) <**> password(p))(mkConnection)
mkConnection <$> user(p) <*> password(p)
ApplicativeFuture
future(discount(_,_))) <*>future(amount) <*> future(rate)
: Future[Double]
ApplicativeList
List(plus1) <*> List(1, 2, 3)
List(2, 3, 4)
ApplicativeList
List(plus1, plus2) <*> List(1, 2, 3)
ratings <*> clients
== List(2, 3, 4, 3, 4, 5)
ApplicativeZipList
List(plus1, plus2, plus3) <*> List(1, 2, 3)
== List(1, 4, 6)
Applicative State
val add = (i: Int) => (j: Int) => i+jval times = (i: Int) => (j: Int) => i*j
// 0 -> 1, 2, 3, 4val 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” loopval 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
xf
zy
x
y z zyx
zy
`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 MonoidAccumulate 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
zy
=> List(x, y, z)
`shape`def shape[A](tree: Tree[A]): Tree[Unit] = map(a => ())(tree)
x
zy
=> .
..
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
zy
=>.
..
List(x, y, z)
Applicative productscase class Product[F1[_], F2[_], A]( first: F1[A], second: F2[A])
F1: Applicative, F2: Applicativedef 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 constructortrait 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 Measuredef 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 mapdef 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 mapdef 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?
Quizzdef 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}
Compositionval 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 vsMonadic composition
Not commutative functions => fusionSeq(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 functionsval 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 commutativeplus1 and times2 are not commutative:
(0 + 1) * 2 != (0 * 2) + 1
Commutative functions => fusionSeq(1,2,3).traverse(plus2 ∎ plus1) == 10
Seq(1,2,3).traverse(plus2) ∎ Seq(1,2,3).traverse(plus1) == 10
Not commutative functions => no fusionSeq(1,2,3).traverse(times2 ∎ plus1) == 22
Seq(1,2,3).traverse(times2) ∎ Seq(1,2,3).traverse(plus1) == 32
Monadic composition:conjecture