Zipper
A data structure with a focus: the subterm at the focus, plus the path back to the root together with everything not on it. Moving the focus and editing at it take constant time, and the type of contexts is the derivative of the structure's type.
A list zipper and a binary tree zipper.
(* List zipper: the elements before the focus (nearest first), and after. *)type 'a lzip = { before : 'a list; focus : 'a; after : 'a list }let left z = match z.before with x :: b -> { before = b; focus = x; after = z.focus :: z.after } | [] -> zlet right z = match z.after with x :: a -> { before = z.focus :: z.before; focus = x; after = a } | [] -> z(* Binary tree zipper: the path back to the root records, at each step,which way we went and the sibling we did not take. *)type 'a tree = Leaf | Node of 'a tree * 'a * 'a treetype 'a step = WentLeft of 'a * 'a tree | WentRight of 'a tree * 'atype 'a tzip = { path : 'a step list; here : 'a tree }let down_left z = match z.here with| Node (l, v, r) -> { path = WentLeft (v, r) :: z.path; here = l } | Leaf -> zlet down_right z = match z.here with| Node (l, v, r) -> { path = WentRight (l, v) :: z.path; here = r } | Leaf -> zlet up z = match z.path with| WentLeft (v, r) :: p -> { path = p; here = Node (z.here, v, r) }| WentRight (l, v) :: p -> { path = p; here = Node (l, v, z.here) }| [] -> zlet rec root z = if z.path = [] then z.here else root (up z)let modify f z = match z.here with Node (l, v, r) -> { z with here = Node (l, f v, r) } | Leaf -> zlet rec show = function| Leaf -> "."| Node (l, v, r) -> "(" ^ show l ^ " " ^ string_of_int v ^ " " ^ show r ^ ")"
Editing the node 3 in place, and moving along a list.
before ((. 1 .) 2 ((. 3 .) 4 .))after ((. 1 .) 2 ((. 300 .) 4 .))list zipper after two rights: before [2; 1], focus 3, after [4]
Read a type as a polynomial in its element type . Lists satisfy , so . A one-hole context is the structure with one taken out, and taking one out of a product follows the product rule:
A one-hole context of a list is two lists, the elements before the hole and after it, which is the list zipper above. For binary trees with values at the nodes, :
The hole is at some node's value, whose two subtrees give the , and above it is a list of steps, each recording a direction (the 2), the value at that node and the sibling subtree . The tree zipper above focuses a whole subtree rather than a value, so its context is the path alone, , with the steps as WentLeft and WentRight.
see also
- Finger treeA persistent sequence with amortized constant-time access at both ends, and concatenation and splitting in logarithmic time. The ends are kept in buffers of one to four elements, and the middle is a finger tree of 2-3 nodes, one level deeper at each step down the spine.
- LensA first-class pair of a getter and a setter for one part of a structure, with laws that make them agree: you get back what you set, setting what you got changes nothing, and a second set overwrites the first. Lenses compose, so a path through nested records is one value.
- Algebraic data typeA type built from sums (a value is one of several cases) and products (a value has several fields at once), possibly recursively. The name comes from counting: the number of values of a sum is the sum of the counts, and of a product the product, so types obey the laws of algebra.
- MonadIn functional programming, a monad is a type constructor m with two operations, return : a -> m a and bind : m a -> (a -> m b) -> m b, satisfying three laws. It lets code with some extra behaviour, such as failure, several results, configuration, state or I/O, be written as a sequence of ordinary steps, with the behaviour defined once in bind. The notion comes from category theory.
- Red-black treeA binary search tree whose nodes are red or black, where no red node has a red child and every path from the root to a leaf has the same number of black nodes. In a functional setting insertion needs one rebalancing function with four symmetric cases, all rewritten to the same shape.
- RepminReplace every leaf of a tree by the tree's minimum, in one traversal. The traversal returns the minimum and the rebuilt tree together, and the rebuilt tree's leaves refer to the minimum the same traversal is still computing. It works because the reference is lazy.
further reading
- [1]G. Huet, “The zipper”, Journal of Functional Programming 7 (1997).
- [2]C. McBride, “The derivative of a regular type is its type of one-hole contexts” (2001).
- [3]M. Abbott, T. Altenkirch, N. Ghani, C. McBride, “Derivatives of containers”, TLCA (2003).