Based on «Scrap Your Zippers: A Generic Zipper for Heterogeneous
Types. Michael D. Adams. WGP '10: Proceedings of the 2010 ACM SIGPLAN
workshop on Generic programming, 2010»
(
http://michaeldadams.org/papers/scrap_your_zippers/).
Compared to the original
syz package, this implementation
(based on
GTraversable) gives more flexibility as to where a
zipper may point to and what is considered as siblings.
Specifically, a zipper may point to any element which
gtraverse
applies its function to.
Example
syz
Consider the classical example: lists. With syz, a list is interpreted
as a right-balanced tree.
>>> let z = fromJust . down' $ toZipper ['a'..'d']
>>> getHole z :: Maybe Char
Just 'a'
The zipper
z points to the first element of the list. Now
let's move to the right:
>>> let z' = fromJust . right $ z
>>> getHole z' :: Maybe Char
Nothing
>>> getHole z' :: Maybe [Char]
Just "bcd"
Instead of pointing to the second element of the list, as one might
expect, the zipper
z' points to the tail of the list. In
order to actually move to the second element, we need another
down':
>>> let z'' = fromJust . down' $ z'
>>> getHole z'' :: Maybe Char
Just 'b'
traverse-with-class
GTraversable-based zippers behave more intuitively in this
regard, thanks to the uniform instance for lists.
>>> let z = fromJust . down' $ toZipper ['a'..'d'] :: Zipper Typeable [Char]
>>> getHole z :: Maybe Char
Just 'a'
So far it's more or less the same as with syz. We needed to add a type
annotation for the zipper itself to clarify the context which should
be available at each hole (
Typeable in this case). Now let's
see what's to the right of us:
>>> let z' = fromJust . right $ z
>>> getHole z' :: Maybe Char
Just 'b'
That is, we jumped right to the second element of the list. Likewise,
>>> let z'' = rightmost z
>>> getHole z'' :: Maybe Char
Just 'd'
So, unlike in
syz, all of the list elements are siblings.