# Tree — foundational binary tree for Bend. # Publish entry for this package. Depends only on Base. # # Encoding: Leaf{} (empty) | Node{left, value, right}. Values live at nodes. # Quantity: Tree is Kind(a), same convention as List/Maybe. # Map uses parallel fork on subtrees (Array.map style) when both sides recurse. import Base type Tree is Kind(a): Leaf{} Node{left: Tree, value: A, right: Tree} def Tree.empty(a, -A: Kind(a)) -> Tree: Leaf{} # Alias for the empty leaf constructor (values live on Node). def Tree.leaf(a, -A: Kind(a)) -> Tree: Leaf{} def Tree.singleton(a, -A: Kind(a), x: A) -> Tree: Node{Leaf{}, x, Leaf{}} def Tree.is_empty(a, -A: Kind(a), t: Tree) -> Bool: match t: case Leaf{}: True{} case Node{l, v, r}: False{} def Tree.size(a, -A: Kind(a), t: Tree) -> Nat: match t: case Leaf{}: 0n case Node{l, v, r}: 1n+Nat.add(Tree.size(a, A, l), Tree.size(a, A, r)) # Template map, same shape as List.map (affine Tree = Tree<&1, A>). # Subtrees mapped in parallel via `nl nr = ... ...`. def Tree.map(~A: Type, ~B: Type, ~f: A -> B, t: Tree) -> Tree: match t: case Leaf{}: Leaf{} case Node{l, v, r}: nl nr = Tree.map(~A, ~B, ~f, l) Tree.map(~A, ~B, ~f, r) Node{nl, f(v), nr} # Catamorphism: ~leaf/~node so both branches can reuse them (tree fan-out). def Tree.fold( ~a: Quant, ~A: Kind(a), ~B: Type, ~leaf: B, ~node: B -> A -> B -> B, t: Tree ) -> B: match t: case Leaf{}: leaf case Node{l, v, r}: node( Tree.fold(~a, ~A, ~B, ~leaf, ~node, l), v, Tree.fold(~a, ~A, ~B, ~leaf, ~node, r) ) # Combiner for reduce: (left ⊕ value) ⊕ right. def Tree.reduce.node(~A: Type, ~f: A -> A -> A, lv: A, x: A, rv: A) -> A: f(f(lv, x), rv) # Monoid-style reduce with zero (~z duplicated across branches). def Tree.reduce( ~a: Quant, ~A: Kind(a), ~f: A -> A -> A, ~z: A, t: Tree ) -> A: Tree.fold(~a, ~A, ~A, ~z, ~Tree.reduce.node(~A, ~f), t) # In-order flattening. def Tree.to_list(a, -A: Kind(a), t: Tree) -> List: match t: case Leaf{}: Nil{} case Node{l, v, r}: List.append(a, A, Tree.to_list(a, A, l), v <> Tree.to_list(a, A, r))