Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

traversal: Tree Operations

All operations respect the children/rebuild contract: rebuild node (children node) == node.

Core Operations

children

Get immediate child nodes in deterministic order.

traversal.children :: Expr -> [Expr]

Example:

# For: { tag = "App"; func = f; arg = x; }
traversal.children node  # => [f, x]

rebuild

Reconstruct node from new children (inverse of children).

traversal.rebuild :: Expr -> [Expr] -> Expr

Example:

newNode = traversal.rebuild node [newFunc newArg]

descend

Apply function to all immediate children, then rebuild.

traversal.descend :: (Expr -> Expr) -> Expr -> Expr

Example:

# Transform all direct children
traversal.descend (n: n + 1) node

Transformations

transform

Bottom-up transformation: f (descend (transform f) node).

traversal.transform :: (Expr -> Expr) -> Expr -> Expr

Example:

# Replace all integers with their double
doubleInts = traversal.transform (node:
  if syntax.isConstant node && syntax.isInt node.contents then
    syntax.mkConstant (syntax.mkInt (node.contents.contents * 2))
  else
    node
);

rewrite

Apply rule bottom-up; null means no change.

traversal.rewrite :: (Expr -> Expr | null) -> Expr -> Expr

Example:

# Simplify constant folding
simplify = traversal.rewrite (node:
  if isBinaryAdd node && bothConstants node then
    syntax.mkInt (evalNode node)
  else
    null  # no change
);

para

Paramorphism: access node and recursive results from children.

traversal.para :: (Expr -> [a] -> a) -> Expr -> a

Example:

# Count all nodes
countNodes = traversal.para (node: childCounts:
  1 + builtins.foldl' (a: b: a + b) 0 childCounts
);

Exploration

universe

All descendant nodes including self.

traversal.universe :: Expr -> [Expr]

Example:

allSymbols = builtins.filter syntax.isSym (traversal.universe ast);

holes

Each child paired with a replacement function.

traversal.holes :: Expr -> [(Expr, Expr -> Expr)]

Example:

# Get all positions where a child can be replaced
holes = traversal.holes ast;
# => [(child1, \replacement -> rebuild node [replacement ...]),
#     (child2, \replacement -> rebuild node [... replacement ...])]

contexts

Every subnode paired with a function to replace it in context.

traversal.contexts :: Expr -> [(Expr, Expr -> Expr)]

Example:

# Get all subexpressions with their context
contexts = traversal.contexts ast;

Children/Rebuild Contract

All traversal operations maintain this invariant:

traversal.rebuild node (traversal.children node) == node

This ensures that operations can freely decompose and reconstruct nodes without losing information.

Use Cases

Deep Transformation

# Replace all variable references
replaceVars = traversal.transform (node:
  if syntax.isSym node then
    syntax.mkSym (replaceName node.contents)
  else
    node
);

Collection

# Collect all function applications
collectApps = traversal.universe >> builtins.filter syntax.isApp;

Analysis

# Check if expression contains a specific pattern
containsAssert = node:
  builtins.any syntax.isAssert (traversal.universe node);