What you probably need here is some sort of accumulator: a variable that you pass through the recursive calls, and each time you increment each time you "assign" the next id.
We thus define our function in terms of a helper function go
. go
will return a 2-tuple: the "labeled" tree, and the next id that we will "dispatch". This will later be used since we define a recursive call:
labelTree :: Tree a -> Tree (a, Int)
labelTree = fst . go 0
where go ...
So go
has type Int -> Tree a -> (Int, Tree (a, Int))
. In case we see a Leaf
, we thus "dispatch" that id, and then return that leaf, together with n + 1
as second part of the tuple, like:
go (Leaf x) n = (Leaf (x, n), n+1)
for a node, we will first dispatch ids to the left subtree, and then take the second item of that tuple as a start to dispatch elements to the right subtree, like:
go (Node l r) n0 = (Node ll lr, n2)
where (ll, n1) = go l n0
(lr, n2) = go r n1
We thus first call go l n0
to label the left subtree, and obtain a 2-tuple (ll, n1)
that contains ll
the labeled left subtree, and n1
the new number to dispatch later. We make a call to go r n1
so we dispatch numbers to the right subtree starting with n1
. Our go
funcion thus returns a new Node
with the labeled subtrees, and the new number to dispatch. This is important for the caller of this function.
So in full, we can label a tree with:
labelTree :: Tree a -> Tree (a, Int)
labelTree = fst . go 0
where go (Leaf x) n = (Leaf (x, n), n+1)
go (Node l r) n0 = (Node ll lr, n2)
where (ll, n1) = go l n0
(lr, n2) = go r n1