No matching definitions.

tur/zip

stdlib/zip.tur

generic zippers: a structural zipper over any tree, and an element cursor.

Two persistent zippers, both pure Turmeric:

  • (ZipLoc T), the STRUCTURAL zipper -- Clojure's clojure.zip, typed. Describe a homogeneous tree type T with three functions (branch?, children, make-node, bundled by zip/ops) and navigate it with zip/down, zip/up, zip/left, zip/right, zip/next, zip/prev; edit it with zip/replace, zip/edit, zip/insert-*, zip/remove; rebuild it with zip/root. zip/rose-zip does this for the stock rose tree (Rose A), and a Plated instance does it for your own type (zip/plated).
  • (ZipCursor S A), the ELEMENT cursor -- a zipper over the A-elements of any container S, described by a (Traversal S A). Step through the elements, edit them in place, and close the cursor back into an S of the same shape. Over zip/cons-traversal it is an O(1) list zipper.

Every location is an immutable value: a move or an edit returns a new one and leaves the old one valid. Unedited subtrees are shared rather than copied, and (zip/root (zip/zipper ops t)) returns t itself.

Moves that Clojure answers with nil at a boundary return Option. Edits that Clojure throws on (an insert or remove at the top, the children of a leaf) panic with Clojure's own message.

Memory: locations, frames and rebuilt nodes are :heap values like (Cons A). Outside a region they live until the process exits; inside with-region a session whose result is a scalar rewinds in one step. There is no zip/free.

Not auto-loaded: (load "stdlib/zip.tur"). See docs/guides/zipper-guide.md.

Since: generic-zipper-plan ZP1

defstruct

ZipOps

(defstruct ZipOps [])

how to navigate a tree type T: the three functions of clojure.zip.

branch? says whether a node can have children, children lists them in order, and make-node rebuilds a node with a new list of children. Build one with zip/ops. :heap :copy, so a location carries it as one word.

Since: generic-zipper-plan ZP1

defstruct

ZipFrame

(defstruct ZipFrame [])

one level of a location's path (internal).

lefts holds the left siblings NEAREST FIRST, pnode the parent as it was on the way down, up the next frame out (the NULL link at the root), rights the right siblings in order, and changed whether anything below this frame was edited -- zip/up rebuilds the parent only then.

Since: generic-zipper-plan ZP1

defstruct

ZipLoc

(defstruct ZipLoc [])

a location in a tree of T: the focused node plus the path to it.

end is Clojure's :end marker, set by zip/next past the last node.

Since: generic-zipper-plan ZP1

defn

zip/-top?

(zip/-top? [T])

internal: is this path link the root's (the NULL link)?

defn

zip/-no-frame

(zip/-no-frame [T])

internal: the root's path link.

defn

zip/-rev-onto

(zip/-rev-onto [T])

internal: xs reversed, in front of acc.

defn

zip/-append1

(zip/-append1 [T])

internal: xs with x added at the end.

defn

zip/-loc

(zip/-loc [T])

internal: a location that is not the end marker.

defn

zip/-frame

(zip/-frame [T])

internal: a path frame.

defn

zip/-mark

(zip/-mark [T])

internal: the frame with changed set; the root link as is.

defn

zip/-depth-acc

(zip/-depth-acc [T])

internal: tail-recursive frame count behind zip/depth.

defn

zip/-ancestors-acc

(zip/-ancestors-acc [T])

internal: the parents of every frame from f out, root first.

defn

zip/ops

(zip/ops [T])

bundle the three functions that describe a tree type T.

branch?can this node have children? A false answer makes the node
a leaf: zip/down gives None and zip/children panics.
childrenthe node's children, in order
make-nodethe node rebuilt with the given children, in order

A (ZipOps T) for zip/zipper.

(defdata Tree :heap :copy (Leaf int) (Node (Cons Tree)))
(def tree-ops
  (zip/ops (fn [t : Tree] : bool (match t (Leaf _) false (Node _) true))
           (fn [t : Tree] : (Cons Tree) (match t (Leaf _) (tnil) (Node cs) cs))
           (fn [t : Tree cs : (Cons Tree)] : Tree (Node cs))))

Since: generic-zipper-plan ZP1

defn

zip/zipper

(zip/zipper [T])

open a zipper at the root of a tree.

opshow to navigate T (see zip/ops)
rootthe tree

The location of root, with an empty path.

(zip/node (zip/zipper tree-ops t))  ; => t

Since: generic-zipper-plan ZP1

defn

zip/node

(zip/node [T])

the node at a location.

za location

The focused node.

(zip/node (zip/zipper tree-ops t))  ; => t

Since: generic-zipper-plan ZP1

defn

zip/branch?

(zip/branch? [T])

can the node at a location have children?

za location

The ops' branch? of the focused node. An empty branch answers true.

(zip/branch? (zip/zipper tree-ops (Leaf 1)))  ; => false

Since: generic-zipper-plan ZP1

defn

zip/children

(zip/children [T])

the children of the node at a location, in order.

Panics with "called children on a leaf node" when zip/branch? is false, as Clojure throws: a leaf and an empty branch are different things.

za location whose node is a branch

The node's children, a (Cons T) -- (tnil) for an empty branch.

(tlength (zip/children (zip/zipper tree-ops t)))  ; => number of children

Since: generic-zipper-plan ZP1

defn

zip/make-node

(zip/make-node [T])

build a node with the location's ops.

za location (only its ops are used)
nodethe node to rebuild
kidsits new children, in order

node rebuilt with kids, by the ops' make-node.

(zip/make-node z (zip/node z) (tnil))  ; => the focused node, childless

Since: generic-zipper-plan ZP1

defn

zip/ancestors

(zip/ancestors [T])

the nodes on the path from the root to a location's parent.

Clojure's path. Each is the node as it was on the way down, so an edit below it is not reflected until zip/up rebuilds it.

za location

The ancestors, root first; (tnil) at the root.

(tlength (zip/ancestors (zip/zipper tree-ops t)))  ; => 0

Since: generic-zipper-plan ZP1

defn

zip/lefts

(zip/lefts [T])

the left siblings of a location, in document order.

za location

The siblings before the node, leftmost first; (tnil) at the root.

(zip/lefts (zip/zipper tree-ops t))  ; => (tnil)

Since: generic-zipper-plan ZP1

defn

zip/rights

(zip/rights [T])

the right siblings of a location, in document order.

za location

The siblings after the node, nearest first; (tnil) at the root.

(zip/rights (zip/zipper tree-ops t))  ; => (tnil)

Since: generic-zipper-plan ZP1

defn

zip/depth

(zip/depth [T])

how many levels a location is below the root.

za location

0 at the root, 1 for its children, and so on.

(zip/depth (zip/zipper tree-ops t))  ; => 0

Since: generic-zipper-plan ZP1

defn

zip/end?

(zip/end? [T])

has zip/next walked past the last node?

za location

true for the end marker zip/next returns after the last node in pre-order, false otherwise.

(zip/end? (zip/zipper tree-ops t))  ; => false

Since: generic-zipper-plan ZP1

defn

zip/down

(zip/down [T])

move to the leftmost child.

za location

(Some loc) at the first child; None on a leaf or an empty branch.

(zip/down (zip/zipper tree-ops (Node (tcons-of (Leaf 1) (tnil)))))
; => (Some loc), with (Leaf 1) in focus

Since: generic-zipper-plan ZP1

defn

zip/up

(zip/up [T])

move to the parent, rebuilding it if anything below was edited.

An unedited parent is returned as it was, with no allocation, so a walk that edits nothing shares every node with the original tree.

za location

(Some loc) at the parent; None at the root.

(zip/up (zip/zipper tree-ops t))  ; => None

Since: generic-zipper-plan ZP1

defn

zip/right

(zip/right [T])

move to the next sibling.

za location

(Some loc) at the right sibling; None at the rightmost child or the root.

(zip/right (zip/zipper tree-ops t))  ; => None

Since: generic-zipper-plan ZP1

defn

zip/left

(zip/left [T])

move to the previous sibling.

za location

(Some loc) at the left sibling; None at the leftmost child or the root.

(zip/left (zip/zipper tree-ops t))  ; => None

Since: generic-zipper-plan ZP1

defn

zip/leftmost

(zip/leftmost [T])

move to the first sibling.

za location

The leftmost sibling's location; z itself when already there or at the root.

(zip/node (zip/leftmost z))  ; => the first child of z's parent

Since: generic-zipper-plan ZP1

defn

zip/rightmost

(zip/rightmost [T])

move to the last sibling.

za location

The rightmost sibling's location; z itself when already there or at the root.

(zip/node (zip/rightmost z))  ; => the last child of z's parent

Since: generic-zipper-plan ZP1

defn

zip/root

(zip/root [T])

zip all the way up and return the (possibly edited) tree.

za location

The root node, with every edited level rebuilt. With no edits it is the very tree the zipper was opened on.

(zip/root (zip/replace (zip/zipper tree-ops t) (Leaf 9)))  ; => (Leaf 9)

Since: generic-zipper-plan ZP1

defn

zip/-deepest-last

(zip/-deepest-last [T])

internal: the last node in pre-order under z (z if a leaf).

defn

zip/-next-up

(zip/-next-up [T])

internal: next from a node with no child and no right sibling.

defn

zip/next

(zip/next [T])

move to the next node in a depth-first, pre-order walk.

Past the last node it returns the END marker: a location at the (rebuilt) root for which zip/end? is true. zip/next of the end marker is the end marker; zip/root of it is the tree.

za location

The next location in pre-order, or the end marker.

(defn visit-all [T] [z : (ZipLoc T)] : T
  (if (zip/end? z) (zip/root z) (visit-all (zip/next z))))

Since: generic-zipper-plan ZP1

defn

zip/prev

(zip/prev [T])

move to the previous node in a depth-first, pre-order walk.

za location

(Some loc) at the previous node in pre-order; None at the root (and at the end marker).

(zip/prev (zip/zipper tree-ops t))  ; => None

Since: generic-zipper-plan ZP1

defn

zip/replace

(zip/replace [T])

replace the node at a location.

za location
nodethe new node

The same location with node in focus; zip/up and zip/root rebuild the levels above it. z itself is unchanged.

(zip/node (zip/replace z (Leaf 40)))  ; => (Leaf 40)

Since: generic-zipper-plan ZP1

defn

zip/edit

(zip/edit [T])

replace the node at a location with a function of it.

za location
fthe edit, applied to the focused node

(zip/replace z (f (zip/node z))).

(zip/edit z (fn [t : Tree] : Tree (Leaf 0)))

Since: generic-zipper-plan ZP1

defn

zip/insert-left

(zip/insert-left [T])

insert a sibling immediately to the left, without moving.

Panics with "Insert at top" at the root, as Clojure throws.

za location below the root
itemthe new sibling

The same location, with item as its new left sibling.

(zip/lefts (zip/insert-left z (Leaf 0)))  ; => ... ending in (Leaf 0)

Since: generic-zipper-plan ZP1

defn

zip/insert-right

(zip/insert-right [T])

insert a sibling immediately to the right, without moving.

Panics with "Insert at top" at the root, as Clojure throws.

za location below the root
itemthe new sibling

The same location, with item as its new right sibling.

(thead (zip/rights (zip/insert-right z (Leaf 0))))  ; => (Leaf 0)

Since: generic-zipper-plan ZP1

defn

zip/insert-child

(zip/insert-child [T])

add a first child to the node at a location, without moving.

Panics with "called children on a leaf node" when the node is not a branch.

za location whose node is a branch
itemthe new leftmost child

The same location, its node rebuilt with item before its children.

(thead (zip/children (zip/insert-child z (Leaf 0))))  ; => (Leaf 0)

Since: generic-zipper-plan ZP1

defn

zip/append-child

(zip/append-child [T])

add a last child to the node at a location, without moving.

Panics with "called children on a leaf node" when the node is not a branch.

za location whose node is a branch
itemthe new rightmost child

The same location, its node rebuilt with item after its children.

(zip/node (zip/rightmost (unwrap (zip/down (zip/append-child z (Leaf 0))))))
; => (Leaf 0)

Since: generic-zipper-plan ZP1

defn

zip/remove

(zip/remove [T])

delete the node at a location.

Panics with "Remove at top" at the root, as Clojure throws.

za location below the root

The location that preceded z in a pre-order walk: the deepest last descendant of its left sibling, or its parent (rebuilt) when z was the first child.

(zip/root (zip/remove z))  ; => the tree without z's node

Since: generic-zipper-plan ZP1

defn

zip/find-next

(zip/find-next [T])

search forward in pre-order for a node satisfying p.

The search starts AT z: z itself is returned when its node satisfies p. To find the following match, search from (zip/next found).

zwhere to start
pthe test, applied to each node

(Some loc) at the first node, from z on in pre-order, satisfying p; None when the walk reaches the end without one.

(zip/find-next (zip/zipper tree-ops t)
               (fn [n : Tree] : bool (match n (Leaf v) (= v 3) (Node _) false)))
; => (Some loc) at (Leaf 3)

Since: generic-zipper-plan ZP2

defn

zip/edit-all

(zip/edit-all [T])

apply f to every node from z on, in pre-order, and return the root.

Clojure's standard edit loop, shipped: edit the node, move with zip/next, stop at the end. Each node is edited BEFORE its children are visited, so the walk descends into what f returned -- the zipper form of Uniplate's transform, top-down. From the root it rewrites the whole tree.

zwhere to start (usually the root location)
fthe edit, applied to each node

The rebuilt root.

(zip/edit-all (zip/zipper tree-ops t)
              (fn [n : Tree] : Tree (match n (Leaf v) (Leaf (* v 2)) (Node _) n)))
; => t with every leaf doubled

Since: generic-zipper-plan ZP2

defstruct

Rose

(defstruct Rose :heap [A] (label A) (kids (Cons (Rose A))))

a rose tree: a label and a list of child trees (Haskell's Data.Tree).

The typed counterpart of Clojure's seq-zip. Every node is a branch -- a leaf is a node with no children -- so zip/down of a leaf is None and zip/append-child works anywhere. kids is a Cons, so zip/down is O(1). Build one with zip/rose or make-struct.

Since: generic-zipper-plan ZP2

defn

zip/rose

(zip/rose [A])

build a rose tree node.

labelthe node's label
kidsits children, in order ((tnil) for a leaf)

A (Rose A).

(zip/rose 1 (tcons-of (zip/rose 2 (tnil)) (tnil)))  ; => 1 with one child, 2

Since: generic-zipper-plan ZP2

defn

zip/rose-ops

(zip/rose-ops [A])

the ZipOps of a rose tree.

Ops whose branch? is always true, children reads kids, and make-node keeps the label and replaces the children.

(zip/zipper (zip/rose-ops) r)  ; same as (zip/rose-zip r)

Since: generic-zipper-plan ZP2

defn

zip/rose-zip

(zip/rose-zip [A])

open a zipper at the root of a rose tree.

rthe tree

(zip/zipper (zip/rose-ops) r).

(.label (zip/node (zip/rose-zip (zip/rose 7 (tnil)))))  ; => 7

Since: generic-zipper-plan ZP2

defclass

Plated

(defclass Plated [a])

a type whose values have children of the same type.

lens's Plated / Uniplate's Uniplate: the three ZipOps functions as an instance, so zip/plated can open a zipper with no ops argument. An instance gives one canonical view of a type; for a second view of the same type, build a ZipOps with zip/ops instead.

athe tree type
(plate-branch? [x :a] :bool)

can x have children?

(plate-children [x :a] :(Cons a))

x's children, in order

(plate-rebuild [x :a kids :(Cons a)] :a)

x rebuilt with kids as its children, in order

(definstance Plated [Tree]
  (plate-branch? [t] (match t (Leaf _) false (Node _) true))
  (plate-children [t] (match t (Leaf _) (tnil) (Node cs) cs))
  (plate-rebuild [t cs] (Node cs)))

Rose

Since: generic-zipper-plan ZP3

definstance

Plated[Rose]

(definstance Plated [Rose])

a rose tree's children are its kids; every node is a branch.

defn

zip/plated-ops

(zip/plated-ops [^Plated T])

the ZipOps a Plated instance describes.

Ops that call the instance's plate-branch?, plate-children and plate-rebuild.

(:: (zip/plated-ops) (ZipOps Tree))

Since: generic-zipper-plan ZP3

defn

zip/plated

(zip/plated [^Plated T])

open a zipper on a value of a Plated type.

rootthe tree

(zip/zipper (zip/plated-ops) root).

(zip/node (zip/plated t))  ; => t

Since: generic-zipper-plan ZP3

defstruct

Traversal

(defstruct Traversal [])

how to visit the A-elements of a container S, and put them back.

parts lists the elements of an S in order; fill returns the same S with its elements replaced, in order. The record mirrors stdlib/lens.tur's Lens. Laws:

(fill s (parts s)) = s
(parts (fill s xs)) = xs, when xs has as many elements as (parts s)

:heap :copy, so a cursor carries it as one word.

Since: generic-zipper-plan ZP4

defstruct

ZipCursor

(defstruct ZipCursor [])

a cursor over the elements of a container, focused on one.

shape is the container as opened, lefts the elements before the focus (nearest first), rights those after it (in order).

Since: generic-zipper-plan ZP4

defn

zip/traversal

(zip/traversal [S A])

build a Traversal from its two functions.

partsthe elements of an S, in order
fillthe S with its elements replaced, in order

A (Traversal S A) for zip/cursor.

(zip/traversal (fn [p : Pair] : (Cons int) ...)
               (fn [p : Pair xs : (Cons int)] : Pair ...))

Since: generic-zipper-plan ZP4

defn

zip/cursor

(zip/cursor [S A])

open a cursor on the first element of a container.

Opening reads every element once (O(n)); after that each move and edit is O(1), and every cursor stays valid.

tthe container's Traversal
sthe container

(Some cursor) focused on the first element; None when s has none.

(zip/cursor (zip/cons-traversal) (tcons-of 1 (tnil)))  ; => (Some c), focus 1

Since: generic-zipper-plan ZP4

defn

zip/cursor-focus

(zip/cursor-focus [S A])

the element a cursor is on.

ca cursor

The focused element.

(zip/cursor-focus c)  ; => the current element

Since: generic-zipper-plan ZP4

defn

zip/cursor-set

(zip/cursor-set [S A])

replace the element a cursor is on.

ca cursor
athe new element

The same position with a in focus; c itself is unchanged.

(zip/cursor-focus (zip/cursor-set c 200))  ; => 200

Since: generic-zipper-plan ZP4

defn

zip/cursor-edit

(zip/cursor-edit [S A])

replace the element a cursor is on with a function of it.

ca cursor
fthe edit

(zip/cursor-set c (f (zip/cursor-focus c))).

(zip/cursor-edit c (fn [x : int] : int (* x 2)))

Since: generic-zipper-plan ZP4

defn

zip/cursor-next

(zip/cursor-next [S A])

move to the next element.

ca cursor

(Some cursor) on the next element; None on the last one.

(zip/cursor-next c)  ; => (Some c2), or None at the last element

Since: generic-zipper-plan ZP4

defn

zip/cursor-prev

(zip/cursor-prev [S A])

move to the previous element.

ca cursor

(Some cursor) on the previous element; None on the first one.

(zip/cursor-prev c)  ; => (Some c0), or None at the first element

Since: generic-zipper-plan ZP4

defn

zip/cursor-close

(zip/cursor-close [S A])

write the elements back into the container.

ca cursor

The container the cursor was opened on, with every element as the cursor now has it, by the Traversal's fill (O(n)). The original container is unchanged.

(zip/cursor-close (zip/cursor-set c 200))  ; => s with that element 200

Since: generic-zipper-plan ZP4

defn

zip/cons-traversal

(zip/cons-traversal [A])

the Traversal of a list: its elements are itself.

A cursor over it is a persistent list zipper with O(1) moves.

A (Traversal (Cons A) A) whose parts is the identity and whose fill returns the new elements as the list.

(zip/cursor (zip/cons-traversal) xs)  ; => a cursor over xs

Since: generic-zipper-plan ZP4

defn

zip/-forest-labels

(zip/-forest-labels [A])

internal: a forest's labels in pre-order, in front of acc.

defstruct

ZipFill

(defstruct ZipFill :heap [A] (built (Cons (Rose A))) (rest (Cons A)))

internal: a rebuilt forest and the labels it did not use.

defn

zip/-fill-forest

(zip/-fill-forest [A])

internal: relabel a forest in pre-order from xs.

A tree keeps its old label once xs runs out, so fill is total.

defn

zip/rose-labels

(zip/rose-labels [A])

the Traversal of a rose tree's labels, in pre-order.

A (Traversal (Rose A) A): parts lists the labels root first, depth first; fill relabels the same shape from a list of labels.

(zip/cursor (zip/rose-labels) r)  ; => a cursor over r's labels

Since: generic-zipper-plan ZP4