
Hi all, After using zippers for a while, I wanted to dig a bit deeper into them. I found there is some relation between Zipper and Comonad, but this confuses me somewhat. After reading a bit more about Comonads [1] and [2], I think I understand them somewhat, and I see how they too are useful for navigating a data structures and giving functions the ability to "look around". What confuses me though, is the relation between these 2. This source [3] mentions all zippers can be made instances of Comonad, and demonstrates how to do this for simple, 1-dimensional (list) zippers. But a comment on [4] says a Zipper by itself is already an application of Comonad. I want to find out which is the case. Looking at the types does not yield me a solution yet. This is Zipper from LYAH: data Tree a = Empty | Node a (Tree a) (Tree a) data Crumb a = LeftCrumb a (Tree a) | RightCrumb a (Tree a) type Breadcrumbs a = [Crumb a] type Zipper a = (Tree a, Breadcrumbs a) This is Comonad from the comonad package (flattened to 1 class): class Functor w => Comonad w where duplicate :: w a -> w (w a) extend :: (w a -> b) -> w a -> w b extract :: w a -> a Now, if [3] is correct, I can write "instance Comonad Zipper". If I understood all this correctly, "extend" becomes "(Zipper a -> b) -> Zipper a -> Zipper b". This gives me something like a "look-around fmap" which sounds useful. If the comment on [4] is correct though, Zipper somehow has a Comonad "built-in", (probably hidden the interaction between Tree and [Crumb]). So in that case, a Zipper would be a (somewhat customized) "instance Comonad Tree" with some extensions. "(Tree a -> b) -> Tree a -> Tree b" seems reasonable. It will build up a new tree with the same shape as the input tree, and allows the mapping function to examine every node _and_ its child nodes. It does not allow "looking up" though. So what do I make of this? It's clear that every Zipper can be a Comonad, and it's probably useful in some cases, but on the other hand, a Zipper already gives the ability to look around and modify (a -> a) things, so the comonadic instance only allows you to do this in a somewhat structure-preserving way. It's still not clear to me whether there is some truth in "a Zipper itself is an application of Comonad". What I looked at above is just a Tree instance, which obviously lost power compared to a full Zipper. But I somehow feel there indeed is a somewhat deeper relation between these 2 compared to just "can be made instance of". Please share your thoughts. Thanks, Mathijs [1] http://blog.sigfpe.com/2006/12/evaluating-cellular-automata-is.html [2] http://blog.sigfpe.com/2008/03/comonadic-arrays.html [3] http://blog.sigfpe.com/2007/01/monads-hidden-behind-every-zipper.html [4] http://gelisam.blogspot.com/2007/04/i-understand-comonads.html

Hi Mathijs On 22 May 2012, at 07:42, Mathijs Kwik wrote:
Hi all,
After using zippers for a while, I wanted to dig a bit deeper into them. I found there is some relation between Zipper and Comonad, but this confuses me somewhat.
After reading a bit more about Comonads [1] and [2], I think I understand them somewhat, and I see how they too are useful for navigating a data structures and giving functions the ability to "look around".
What confuses me though, is the relation between these 2. This source [3] mentions all zippers can be made instances of Comonad, and demonstrates how to do this for simple, 1-dimensional (list) zippers. But a comment on [4] says a Zipper by itself is already an application of Comonad. I want to find out which is the case. Looking at the types does not yield me a solution yet.
[..] Once upon a time, I wrote this message: http://www.haskell.org//pipermail/libraries/2010-July/013843.html which may serve as grist to your mill. The key points (1) Inductive datatypes can usually be seen as instances of data Mu f = In (f (Mu f)) where f is a Functor explaining the structure of one node, with the parameter of f standing for the node's children. See how Mu f instantiates f's parameter with Mu f, meaning that the children are recursive subobjects? (2) The derivative f' of such an f characterizes "f-structures with one element missing", so a one-hole context in a Mu f is given by the type [f' (Mu f)], being a series of nodes, each of which has one child missing, but whose other children are still intact. The whole zipper is given by ([f' (Mu f)], Mu f) (3) The construction Focusf x = (f' x, x), representing an f-node with one child held separately, is always comonadic. The counit gives the separated child, discarding the context. The cojoin decorates each element with its own context, showing the decompositions you could get by "moving the cursor". Sorry to be so brief and example-free -- terrible hurry Conor

Hi Mathijs, On Tue, May 22, 2012 at 8:42 AM, Mathijs Kwik wrote:
After using zippers for a while, I wanted to dig a bit deeper into them. I found there is some relation between Zipper and Comonad, but this confuses me somewhat.
You might also take a look at: Comonadic functional attribute evaluation Tarmo Uustalu, Varmo Vene Trends in Functional Programming 6 (2007), pp. 145-162 http://www.cs.ioc.ee/~tarmo/papers/tfp05.pdf Regards, Sean
participants (3)
-
Conor McBride
-
Mathijs Kwik
-
Sean Leather