This library implements zippers as published in Functional Pearl, The Zipper by Gérard Huet. It also provides a namespace (com.hapgood.zipii
) with a high degree of compatibility with the original Clojure implementation in clojure.zip.
In rough priority order, the goals of this implementation are as follows:
- Implement a full-featured basic Zipper as described by Huet.
- Implement an equally full-featured Scar Zipper using the same API as the basic Zipper.
- Have no dependencies on external libraries.
- Support serialization of the zipper (the Loc record and its basis components).
- Use "modern" and idiomatic Clojure, specifically:
- Use data-laden exceptions where appropriate.
- Leverage protocols to allow for extensions.
- As in the original Clojure implementation, support user-selectable data structures as the branch (interior) nodes of the tree.
- Be host-agnostic.
- Support a
down-to
function applicable to navigating within Clojure's indexed data collections (map-like and vector-like). - Provide a
clojure.zip
compatibility mode in the com.hapgood.zipii
namespace. - Perform well updating trees.
- Perform well for navigating trees.
- Contain the Clojure-specific implementation details into as small a volume as possible (hopefully this improves the readability of the code if one uses Huet's publication as a starting point).
I've also included a test suite which has been immensely useful as I have iterated on the design.
Things I would like:
- Better documentation -especially around the Zip protocol's differences from the original Clojure implementation which opts for three user-supplied functions for the same functionality.
- More comprehensive test coverage.
- Better understanding of the original xml-zip function ... is it really so open to defining a node that an integer is a branch?
- A ClojureScript port. This should not be hard as I have avoided any Java interop and there is no dependency on external libs.
- A better implementation of the clojure.zip-compatible
remove
function -my implementation is adequate but not likely optimal. - A performance comparison between the protocol-based approach I use and the original clojure.zip approach.
- Some feedback on the value of foregoing tracking changes (as compared to the original Clojure implementation). Is the performance hit bad for real-world work? Is there a clean way to have my cake (no
changed?
attribute) and eat it (high read-only performance) too?