660 likes | 804 Views
( eip ). The Essence of the Iterator Pattern. Functor. Computation. For loop. APPLICATIVE. 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].
E N D
(eip) The Essence of the IteratorPattern
Functor Computation For loop APPLICATIVE 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 Before getUser(props: Properties): String getPassword(props: Properties): String getConnection(user: String, pw: String): Connection = { if (user != null && pw != null) .... } getConnection(getUser(p), getPassword(p))
Applicative After getUser(props: Properties): Option[String] getPassword(props: Properties): Option[String] getConnection(user: String, pw: String): Connection = { if (user != null && pw != null) .... } getConnection(?, ?)
Applicative f(a, b) How? f(K[a], K[b])
Use Pointed f(a:A, b:B): C point fk: K[A => B => C]
Applicative K[A => B] <*> K[A] == K[B] Apply the function
Applicative K[A => B => C] <*> K[A] <*> K[B] == K[B => C] <*> K[B] == K[C] Currying ftw!
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) == List(2, 3, 4, 3, 4, 5) • ratings <*> clients
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 current state +1=2 +1=3 (add <$> s1 <*> s1)(1) == (3, 5) add 2 previous states +1=2 +1=3 (times <$> s1 <*> s1)(1) == (3, 6) multiply 2 previous states
Monad, remember? Unit def unit[A](a: =>A): M[A] Bind defbind[A, B](ma: M[A])(f: A => M[B]): M[B]
Monad => Applicative Point defpoint(a: =>A) = Monad[M].unit(a) Apply 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]
The “for” loop val basket =Basket(orange, apple)var count =0val juices =Basket[Juice]() for(fruit <- basket){ count = count +1juices.add(fruit.press)} accumulation “mapping” same container for the result
Traverse Traversable def traverse(f: A => F[B]): T[A]=> F[T[B]] Applicative Same structure
Traverse a List List(x, y, z): List[A] f: A => F[B]
Traverse a List Apply ‘f’ to ‘z’ F(::) <*> F(z) <*> F(Nil) “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 • f x y z x x y z y z y z
`sequence` def sequence[F: Applicative]: T[F[A]]=> F[T[A]] = • traverse(identity)
`sequence` Execute concurrently? • val examples: Seq[Example] = • Seq(e1, e2, e3) Sequence of promises • val executing: Seq[Promise[Result]] = • examples.map(e => promise(e.execute)) • val results: Promise[Seq[Result]] = executing.sequence Promise of a sequence
Measure with Monoids traitMonoid[A] { val zero: A; def append(a: A, b: A): A } def measure[T: Traversable, M : Monoid] (f: A => M): T[A] => M Count elements: IntMonoid Accumulate elements: List Monoid
`measure` def measure[T: Traversable, M : Monoid] (f: A => M) = traverse(a => f(a))
`Const` “Phantom “ type case class Const[M, +A](value: M) 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 Unit def unit[A](a: =>A) = Const(Monoid[M].zero) Bind defbind[A, B](ma: Const[M, A]) (f: A => Const[M, B]) = => but no value `a: A` to be found!
`measure` Sum up all sizes defsumSizes[A : Size](seq: Seq[A]) = measure(a => Size[A].size(a))(seq) Collect all sizes defcollectSizes[A : Size](seq: Seq[A]) = measure(a => List(Size[A].size(a)))(seq)
`contents` def contents[A](tree: Tree[A]): List[A] = • measure(a => List(a))(tree) => x List(x, y, z) y z
`shape` def shape[A](tree: Tree[A]): Tree[Unit] = map(a => ())(tree) => x . y z . . 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)) List(x, y, z) => x . y z . . Not very efficient…
Applicative products case classProduct[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(())valcontentsAndShape: • A => Product[Const[List[A], _], Ident[_], *] = • contents ⊗ shape • tree.traverse(contentsAndShape)
Type indifference One parameter type constructor • trait Apply[F[_]]{def <*>[A, B](f: F[A => B]): F[A]=> F[B]} List[Int]: MonoidApplicative => Const[List[Int], _] • ({type l[a]=Const[List[Int], a]})#l
Type indifference Anonymous type • ({type l[a]=Const[List[Int], a]})#l Type member • ({type l[a]=Const[List[Int], a]})#l Type projection • ({type l[a]=Const[List[Int], a]})#l • type ApplicativeProduct = • ({type l[a]=Product[Const[List[A],_],Ident[_],a]})#l
Type indifference Measure • def measure[M : Monoid](f: T => M): M = • traverse(t => Monoid[M].unit(f(t))).value For real… • 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
`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.toStringtree.collect(count, map).apply(0) (2,Bin(Leaf("1"),Leaf("2")))