Contents

Sometimes you want to manipulate a location inside a data structure,
rather than the data itself. For example, consider a simple binary tree type:

data Tree a = Fork (Tree a)(Tree a)| Leaf a

and a sample tree t:

t = Fork (Fork (Leaf 1)(Leaf 2))(Fork (Leaf 3)(Leaf 4))

Each subtree of this tree occupies a certain location in the tree taken as a whole. The location consists of the subtree, along with the rest of the tree, which we think of the context of that subtree. For example, the context of

Leaf 2

in the above tree is

Fork (Fork (Leaf 1)@)(Fork (Leaf 3)(Leaf 4))

where @ marks the spot that the subtree appears in. One way of expressing this context is as a path from the root of the tree to the required subtree. To reach our subtree, we needed to go down the left branch, and then down the right one. Note that the context is essentially a way of representing the tree, "missing out" a subtree (the subtree we are interested in). (A naive implementation inspired directly by the graphical representation — using

Tree (Maybe a)

instead of

Tree (Maybe a)

— would lose an essential point: the focus is unique.)

We can represent a context as follows:

data Cxt a = Top | L (Cxt a)(Tree a)| R (Tree a)(Cxt a)

L c t represents the left part of a branch of which the right part was t and whose parent had context c. The R constructor is similar. Top represents the top of a tree.

Remarks:

In fact, this is equivalent to a list, whose elements are the appropriate collateral trees, each element labeled with the information which direction was chosen:

type Context a =[(Direction, Tree a)]

data Direction = Lft | Rght

Of course, also here, we propagate from @ towards the root.

Note that in the original paper, Huet dealt with B-trees (ones where nodes have arbitrary numbers of branches), so lists are used instead of the (Tree a) parameters.

Using this datatype, we can rewrite the sample context above in proper Haskell:

R (Leaf 1)(L Top (Fork (Leaf 3)(Leaf 4)))

Note that the context is actually written by giving the path from the subtree to the root (rather than the other way round):

c0

c1

c2

Top

Or the more deconstructed representation:

where c0, c1 are the appropriate correspondents of the c0, c1 of the previous image. It is the empty list that represents c2.

2 Alternative formulation

The dual of Huet zipper is generic zipper -- which is a derivative of
a traversal function rather than that of a data structure.
Unlike Huet zipper,
generic zipper can be implemented once and for all data structures,
in the existing Haskell.
Generic Zipper and its applications