I’m trying to understand the traverseImpl implementation in scalaz-seven:
def traverseImpl[F[_], A, B](l: List[A])(f: A => F[B])(implicit F: Applicative[F]) = {
DList.fromList(l).foldr(F.point(List[B]())) {
(a, fbs) => F.map2(f(a), fbs)(_ :: _)
}
}
Can someone explain how the List interacts with the Applicative? Ultimately, I’d like to be able to implement other instances for Traverse.
An applicative lets you apply a function in a context to a value in a context. So for instance, you can apply
some((i: Int) => i + 1)tosome(3)and getsome(4). Let’s forget that for now. I’ll come back to that later.List has two representations, it’s either
Nilorhead :: tail. You may be used to fold over it usingfoldLeftbut there is another way to fold over it:Given
List(1, 2)we fold over the list applying the function starting from the right side – even though we really deconstruct the list from the left side!This can be used to compute the length of a list. Given
List(1, 2):This can also be used to create another list:
So given an empty list and the
::function we were able to create another list. What if our elements are in some context? If our context is an applicative then we can still apply our elements and::in that context. Continuing withList(1, 2)andOptionas our applicative. We start withsome(List[Int]()))we want to apply the::function in theOptioncontext. This is what theF.map2does. It takes two values in theirOptioncontext, put the provided function of two arguments into theOptioncontext and apply them together.So outside the context we have
(2, Nil) => 2 :: NilIn context we have:
(Some(2), Some(Nil)) => Some(2 :: Nil)Going back to the original question:
I am not sure why the difference
DListis used. What I see is that it uses trampolines so hopefully that makes this implementation work without blowing the stack, but I have not tried so I don’t know.The interesting part about implementing the right fold like this is that I think it gives you an approach to implement traverse for algebric data types using catamorphisms.
For instance given:
Fold would be defined like this (which is really following the same approach as for
List):And traverse would use that
foldwithF.point(Leaf)and apply it toNode.apply. Though there is noF.map3so it may be a bit cumbersome.