essence of the iterator pattern

23
Essence of the iterator pattern Markus Klink, @joheinz, [email protected]

Upload: markus-klink

Post on 15-Jul-2015

552 views

Category:

Software


5 download

TRANSCRIPT

Page 1: Essence of the iterator pattern

Essenceof theiterator patternMarkus Klink, @joheinz, [email protected]

Page 2: Essence of the iterator pattern

Goal“Imperative iterations using the pattern have two simultaneous aspects: mapping and accumulating.”Jeremy Gibbons & Bruno C. d. S. Oliveira

Markus Klink, @joheinz, [email protected]

Page 3: Essence of the iterator pattern

Functional mapping and accumulatingtrait Traverse[F[_]] extends Functor[F] with Foldable[F] { def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]}

trait Applicative[F[_]] extends Functor[F] { def ap[A,B](fa: F[A])(f: F[A => B]) : F[B] def pure(a: A) : F[A] def map[A,B](fa: F[A])(f: A => B) : F[B] = ap(fa)(pure(f))}

Traverse takes a structure F[A], injects each element via the function f: A => G[B] into an Applicative G[_] and combines the results into G[F[B] using the applicative instance of G.Markus Klink, @joheinz, [email protected]

Page 4: Essence of the iterator pattern

A closer looktrait Foldable[F[_]] { def foldRight[A, B](fa: F[A], z: => B)(f: (A, B) => B): B}

trait Traverse[F[_]] extends Functor[F] with Foldable[F] { def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]}

Look at the similarities!

Markus Klink, @joheinz, [email protected]

Page 5: Essence of the iterator pattern

Traversing is "almost" like Folding:» Look! No zero element:

def foldRight[A, B](fa: F[A], z: => B)(f: (A, B) => B): B def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]

» Look! B is wrapped in an Applicative and our F:

def foldRight[A, B](fa: F[A], z: => B)(f: (A, B) => B): B def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]]

Markus Klink, @joheinz, [email protected]

Page 6: Essence of the iterator pattern

Accumulating

Markus Klink, @joheinz, [email protected]

Page 7: Essence of the iterator pattern

val l = List(1,2,3)val result: Future[List[String]] = l.traverse(a => Future.successful { a.toString })

How?

// traverse implementation of List, G[_] is the future override def traverse[G[_]: Applicative, A, B](fa: List[A])(f: (A) => G[B]): G[List[B]] = { val emptyListInG: G[List[B]] = Applicative[G].pure(List.empty[B]) // if the list is empty we need a Future { List.empty() } // f(a) gives us another G[B], which we can inject into B => B => Future[List[B]] foldRight(fa, emptyListInG) { (a: A, fbs: G[List[B]]) => Applicative[G].apply2(f(a), fbs)(_ +: _) } }

// applicative instance of Future, example... override def ap[A,B](F: => Future[A])(f: => Future[A => B]) : Future[B] = { for { a <- F g <- f } yield { g(a) } }

Markus Klink, @joheinz, [email protected]

Page 8: Essence of the iterator pattern

Gibbons & Oliveira claim that we can do:val x : List[Char]= "1234".toListval result : Int = x.traverse(c => ???)assert(4 == result)

The claim is that we can accumulate values, or write the length function just with Traverse/Applicative

Markus Klink, @joheinz, [email protected]

Page 9: Essence of the iterator pattern

How?» we need a result type G[List[Int]] which equals

Int

» G needs to be an applicative

» we need to calculate a sum of all the values.

» we need a zero value in case the list is empty, because of ...

val emptyListInG: G[List[B]] = Applicative[G].pure(List.empty[B])

Markus Klink, @joheinz, [email protected]

Page 10: Essence of the iterator pattern

Each Monoid gives rise to an applicativetrait Monoid[F] extends Semigroup[F] { self => /** * the identity element. */ def zero: F

def applicative: Applicative[Lambda[a => F]] = new Applicative[Lambda[a => F]] { // mapping just returns ourselves override def map[A, B](fa: F)(f: A => B) : F = fa // putting any value into this Applicative will put the Monoid.zero in it def pure[A](a: => A): F = self.zero // Applying this Applicative combines each value with the append function. def ap[A, B](fa: => F)(f: => F): F = self.append(f, fa) }

Markus Klink, @joheinz, [email protected]

Page 11: Essence of the iterator pattern

How 2Part of the trick is this type: Applicative[Lambda[a => F]]!It means that we throw everything away and create a type G[_] which behaves like F. So...

val x : List[Char]= "1234".toList val charCounter : Applicative[Lambda[a => Int]] = Monoid[Int].applicative

// we just "reversed" the parameters of traverse // the summing is done automatically via append charCounter.traverse(x)(_ => 1)

Markus Klink, @joheinz, [email protected]

Page 12: Essence of the iterator pattern

Counting linesIn the previous example we assigned each char in the list to a 1 and let the Monoid do the work.

val x : List[Char] = "1233\n1234\n" val lineCounter : Applicative[Lambda[a => Int]] = Monoid[Int].applicative

lineCounter.traverse(x){c => if (c == '\n') 1 else 0 }

Markus Klink, @joheinz, [email protected]

Page 13: Essence of the iterator pattern

Products of ApplicativesDoing both at the same time within a single traversal

val x : List[Char]= "1234\n1234\n" val counter : Applicative[Lambda[a => Int]] = Monoid[Int].applicative val charLineApp : Applicative[Lambda[a => (Int, Int)]] = counter.product[Lambda[a => Int](counter)

val (chars, lines) = counter.traverse(x){c => (1 -> if (c == '\n') 1 else 0 }

Markus Klink, @joheinz, [email protected]

Page 14: Essence of the iterator pattern

Counting wordsCounting words cannot work on the current position alone. We need to track changes from spaces to non spaces to recognize the beginning of a new word.

def atWordStart(c: Char): State[Boolean, Int] = State { (prev: Boolean) => val cur = c != ' ' (cur, if (!prev && cur) 1 else 0) } val WordCount : Applicative[Lambda[a => Int]] = State.stateMonad[Boolean].compose[Lambda[a => Int](counter) val StateWordCount = WordCount.traverse(text)(c => atWordStart(c)) StateWordCount.eval(false)

Markus Klink, @joheinz, [email protected]

Page 15: Essence of the iterator pattern

Using the product of all 3 counters to implement wc val AppCharLinesWord: Applicative[Lambda[a => ((Int, Int), State[Boolean, Int])]] = Count // char count .product[Lambda[a => Int]](Count) // line count .product[Lambda[a => State[Boolean, Int]]](WordCount) // word count

val ((charCount, lineCount), wordCountState) = AppCharLinesWord.traverse(text)((c: Char) => ((1, if (c == '\n') 1 else 0), atWordStart(c))) val wordCount: Int = wordCountState.eval(false)

Markus Klink, @joheinz, [email protected]

Page 16: Essence of the iterator pattern

Collecting some stateand modifying elements

Markus Klink, @joheinz, [email protected]

Page 17: Essence of the iterator pattern

// start value public Integer count = 0; public Collection<Person> collection = ...; for (e <- collection) { // or we use an if count = count + 1; // side effecting function // or we map into another collection e.modify(); }

Obviously in a functional programming language, we would not want to modify the collection but get back a new collection.We also would like to get the (stateful) counter back.Markus Klink, @joheinz, [email protected]

Page 18: Essence of the iterator pattern

def collect[G[_]: Applicative, A, B](fa: F[A])(f: A => G[Unit])(g: A => B): G[F[B]] = { val G = implicitly[Applicative[G]] val applicationFn : A => G[B] = a => G.ap(f(a))(G.pure((u: Unit) => g(a))) self.traverse(fa)(applicationFn) }

def collectS[S, A, B](fa: F[A])(f: A => State[S, Unit])(g: A => B): State[S, F[B]] = { collect[Lambda[a => State[S, a]], A, B](fa)(f)(g) }

val list : List[Person] = ... val stateMonad = State.stateMonad[Int] val (counter, updatedList) = list.collectS{ a => for { count <- get; _ <- put(count + 1) } yield ()}(p => p.modify()).run(0)

Markus Klink, @joheinz, [email protected]

Page 19: Essence of the iterator pattern

Modifying elementsdepending on some state

Markus Klink, @joheinz, [email protected]

Page 20: Essence of the iterator pattern

// start value public Collection<Person> collection = ...; for (e <- collection) { e.modify(stateful()); }

Now the modification depends on some state we are collecting.

Markus Klink, @joheinz, [email protected]

Page 21: Essence of the iterator pattern

def disperse[G[_]: Applicative, A, B, C](fa: F[A])(fb: G[B], g: A => B => C): G[F[C]] = { val G = implicitly[Applicative[G]] val applicationFn: A => G[C] = a => G.ap(fb)(G.pure(g(a))) self.traverse(fa)(applicationFn) }

def disperseS[S, A, C](fa: F[A])(fb: State[S, S], g: A => S => C) : State[S,F[C]] = { disperse[Lambda[a => State[S, a]], A, S, C](fa)(fb, g) }

Markus Klink, @joheinz, [email protected]

Page 22: Essence of the iterator pattern

THANKSMarkus Klink, @joheinz, [email protected]

Page 23: Essence of the iterator pattern

Some resourceshttp://etorreborre.blogspot.de/2011/06/essence-of-iterator-pattern.html

Markus Klink, @joheinz, [email protected]