How to write function for N-ary tree traversal in Haskell
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
An N-ary tree is a tree where each node can have any number of children, so traversal functions are naturally recursive in Haskell. The cleanest way to write them is to define a recursive tree type and then express each traversal order in terms of the node value plus recursive work over the list of children.
Define the Tree Type Clearly
A simple N-ary tree definition is enough for most traversal problems.
Each Node stores one value and a list of child trees. The list may be empty, which naturally represents a leaf node.
Example tree:
Once the type is defined this way, most traversals become short recursive functions.
Write Preorder Traversal
Preorder means "visit the current node, then visit each child subtree."
This reads almost like the definition of preorder itself:
- emit the current value
- recursively traverse each child
- concatenate the results
For sampleTree, preorder sampleTree returns [1,2,5,6,3,4,7].
Write Postorder Traversal
Postorder means "visit all children first, then visit the current node."
That small change in concatenation order completely changes the traversal result. On the sample tree, the output becomes [5,6,2,3,7,4,1].
This is a good example of how Haskell rewards direct translation of the traversal rule into code.
Breadth-First Traversal Needs a Queue
Depth-first traversals fit plain recursion naturally. Breadth-first traversal is easier if you model a queue explicitly. Data.Sequence works well for that.
Here the queue starts with the root node. Each step removes the front node, emits its value, and appends its children to the back. That produces level-order traversal.
For sampleTree, bfs sampleTree returns [1,2,3,4,5,6,7].
Generalize Traversal Thinking
Once you understand the recursive shape, many tree functions become small variations on the same theme:
- collecting values
- counting nodes
- searching for a match
- mapping a function over the tree
For example, counting nodes is just:
That is useful because it reinforces the real pattern: solve the current node, then combine results from all child subtrees.
Prefer Clear Recursion Before Over-Abstraction
It is tempting to jump straight to folds, zippers, or type class abstractions. Those are powerful, but if the task is simply "write a traversal," the straightforward recursive version is usually the best first answer.
Once the traversal logic is correct and tested, you can refactor repeated patterns into folds or other reusable helpers. Writing the explicit version first keeps the control flow obvious and makes debugging much easier.
Common Pitfalls
- Defining the tree type in a way that makes leaf nodes awkward to represent.
- Mixing up preorder and postorder by placing the current value on the wrong side of the recursive call.
- Trying to write breadth-first traversal with plain recursion and no queue structure.
- Using repeated list append in the wrong place and accidentally making the traversal less efficient.
- Over-abstracting before the recursive structure of the traversal is fully understood.
Summary
- An N-ary tree in Haskell is naturally modeled as a value plus a list of child trees.
- Preorder and postorder are simple recursive functions built with
concatMap. - Breadth-first traversal is easier with an explicit queue such as
Data.Sequence. - Many other tree algorithms follow the same recursive shape.
- Write the direct recursive solution first, then generalize only if the code really benefits from it.

