-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Zippers #184
Comments
Here’s my current thoughts on generic zippers, and how to implement them. I would appreciate any comments, questions, and corrections :) TOC: BackgroundThis section briefly (badly) introduces the various concepts I am using to create the Zipper implementation. It is largely inspired by 1 2. ZippersA Zipper is a data structure that stores a focus and a context. For the sake of example, consider a binary tree: In this case, the focus of the zipper is the current node and its children and the context is the rest of the tree, represented as an upward path from the root of the tree to the focus. In code, this looks like this: type Zipper a = (Node a, Context a)
data Node a = Leaf a
| Branch (Node a) (Node a)
data Context a = Top
| Left (Context a) (Tree a)
| Right (Tree a) (Context a) The context The context and node combined contains all that is needed to reconstruct the tree. For example, this function goes up a level: up :: (Node a, Context a) -> Maybe (Node a, Context a)
up (node, Top) = Nothing
up (node, (Left parentCtx sibling)) = Just ((Branch node sibling), (parentCtx))
up (node, (Right sibling parentCtx)) = Just ((Branch sibling node), (parentCtx))
-- see https://wiki.haskell.org/Zipper for down, left, right Usual operations are The advantages of this approach is that replacing the focus and traversal to siblings is constant time. For details, see the original paper by Huet, 3 or the many good tutorial style explanations online. 4 5 6 Zippers as holes in typesAnother, equivalent, view of the Zipper’s context is that it represents a one element hole in a data-structure. n general, the type of “one holed contexts” is the derivative (in the Leibnitz sense) of the type of the data structure itself. This allows for the mechanical derivation of Zippers on arbitrary types. The disadvantage of this is that the type theory is too confusing for me, and I am not sure we can do this in the Rust type system. Zippers as a suspended walkKiselyov Zippers 7 8 view Zippers as pausing a traversal over the entire data structure. In other words, Zippers can be viewed as a next function (continuation) and a current node. There is lots of cool and complicated stuff about this on his website 9 and in this blog series 10 - including concurrent zippers which allow multiple cursors to navigate a data structure at once. Outline to my approachMy priorities are:
Contexts in the standard Zipper implementation are good, but each enum variant requires manual implementation for each function. However, Using lists and the reconstruction function, we can make the context for tree shaped data: type Zipper<A: Uniplate> = (Context<A>,A);
struct Context<A: Uniplate> {
predecessors: Vec<A>,
sucesssors: Vec<A>,
parent: Box<Option<Context>>,
reconstruct_fn: Box<Fn<A,Vec<A>,A>>
} This gives all information needed to reconstruct the tree and move up, down, left and right. An alternative approach would be storing and moving in terms of one hole context functions, where we store the children as part of the reconstruction function. EDIT: The simplifications i made makes stuff like finding an A inside a B inside an A when traversing over A's hard, but Biplates might fix that, and we probably won't need that anyways. Footnotes |
Overthought things somewhat again, but I think this could work. The primary open questions I am unsure about are algorithmic complexity, whether storing all those closures will be horrible or not, and whether uniplate actually reduces things down to a tree like I think. |
I have a plan for this, but its difficult to explain until I do it.The text was updated successfully, but these errors were encountered: