Skip to content

Instantly share code, notes, and snippets.

@etorreborre
Forked from pchiusano/traverse.scala
Created March 23, 2017 16:38
Show Gist options
  • Save etorreborre/fcc662843e32f19af631c841f6e6c87e to your computer and use it in GitHub Desktop.
Save etorreborre/fcc662843e32f19af631c841f6e6c87e to your computer and use it in GitHub Desktop.
Another encoding of `Traverse` in terms of `mapAccumulate`
/**
* In this version of `Traverse`, sequencing always goes through `List` (or some other canonical sequence type),
* which can be done in a stack-safe manner using a balanced fold as in https://gist.github.com/pchiusano/7667597.
* It's quite nice that `sequence` and `traverse` are now derived functions.
*/
trait Traverse[F[_]] extends Functor[F] {
/** Inefficient but correct implementation of `toList` in terms of `mapAccumulate`. */
def toList[A](f: F[A]): List[A] = mapAccumulate(f, List())((a, rbuf) => (a, a :: rbuf))._2.reverse
/** The only function that must be implemented. Must be consistent with `map`. */
def mapAccumulate[S,A](f: F[A], s0: S)(g: (A,S) => (B,S)): (F[B], S)
def sequence[G[_],A](f: F[G[A]])(implicit G: Applicative[G]): G[F[A]] = {
val gs: List[G[A]] = toList(f)
val fgs: G[List[A]] = List.sequence(gs)(G)
G.map(fgs) { (labels: List[A]) =>
mapAccumulate(f, labels)((_, labels) => (labels.head, labels.tail) // a bit hacky, but safe assuming toList is lawful
// NB: in a DT language or maybe via path dependent types, probably some way to have typechecker verify safety of this
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment