Programming languages should have a tree traversal primitive
Scope of the Proposed Primitive
- Many argue a “tree traversal primitive” is just a specialized iterator and should live in libraries, not in language syntax.
- Objection: making it a primitive is language bloat, especially in already feature-heavy languages like C++.
- Counterpoint: trees (and graphs) are fundamental; ergonomic traversal syntax could enable better default patterns and optimizations (e.g., parallelism).
Traversal Orders and Flexibility
- Critics note the primitive quickly becomes underspecified:
- DFS vs BFS, and then pre-/in-/post-order variants.
- Need to skip branches, terminate early, or traverse in unusual orders.
- By the time all options are encoded, the primitive looks like a general traversal framework—essentially the code you’d write anyway.
Existing Abstractions in Functional / Declarative Worlds
- Functional languages already have strong patterns:
- Algebraic data types + recursion; typeclasses like
Functor,Foldable,Traversable. - Recursion schemes (catamorphisms, hylomorphisms) to systematically define traversals.
- Optics (lenses, prisms, traversals) and zippers for composable navigation and updates.
- Algebraic data types + recursion; typeclasses like
- Clojure examples:
tree-seq,clojure.walk, zippers; SQL CTEs and logic/relational languages cited as tree/graph-friendly models. - Some say this is exactly what the article wants, just as library-level abstractions rather than compiler magic.
Iterators, Generators, and Coroutines
- In imperative languages, the dominant view is: define iterators/generators:
- C++ iterators, C++20/23 coroutines & generators, C#
IEnumerable+yield, Python/Dart generators, Rust’ssuccessorsandControlFlow. - Tree-specific traversal can be implemented once per structure and reused with normal
forloops.
- C++ iterators, C++20/23 coroutines & generators, C#
- Several concrete patterns are shown: explicit stacks/queues for DFS/BFS, generic “successor” functions, or iterator helpers.
Recursion, Stack Limits, and Performance
- Recursive traversals are easy to write but risk stack overflows; explicit stacks or generator-based state machines avoid this.
- Some suggest fixing stack limitations or using tail-call optimization; others note many traversals aren’t tail-recursive.
- Hardware/cache behavior and representation choice (binary vs general trees, parent pointers, indirection) complicate any “universal” primitive.
Design Sentiment
- Overall: strong skepticism that tree traversal deserves special syntax.
- Preference: powerful, composable library abstractions (iterators, optics, recursion schemes) over new control-flow primitives.