List unfold 2026-10-24
Generate a list from a state: a step returning
Nothing terminates; Just (x,s') emits x and continues from s'. Thus unfoldr step s = case step s of Nothing -> []; Just (x,s') -> x : unfoldr step s'. A list unfold decomposes the evolving state, whereas a list fold consumes existing constructors. Productivity depends on each demanded constructor becoming available, not merely on using lazy evaluation.The test consumes a state and returns
Bool; the output function maps a state to an element; and the transition preserves the state type. Hence the most general polymorphic type signature isunfold :: (s -> Bool) -> (s -> a) -> (s -> s) -> s -> [a]Equivalently, . The state and emitted element can have unrelated types. A list unfold produces the next element from the current state, then advances to a new state.
Generate the successive states using the Haskell iterate function, keep those before the stopping test becomes true using the Haskell takeWhile function, then map the output function:
unfoldU :: (s -> Bool) -> (s -> a) -> (s -> s) -> s -> [a]
unfoldU p f g = map f . takeWhile (not . p) . iterate gThe missing expression is therefore . On states , the first state satisfying
p is excluded, just as in the recursive list unfold. If no state satisfies p, the result is an infinite lazy list, provided each demanded test, output and transition terminates.A list unfold needs an explicit state describing the unvisited work. Use a list of pending rose trees, with the next tree first. Emit that tree's root label, and replace it by its children followed by the remaining pending trees:
dftU :: RTree a -> [a]
dftU t = unfold null label advance [t]
where
label :: [RTree a] -> a
label (Node x _ : _) = x
advance :: [RTree a] -> [RTree a]
advance (Node _ children : rest) = children ++ restThe boxed state transition is , with output . The stopping test prevents either helper from being applied to
[].The loop invariant is that concatenating the depth-first traversals of the pending trees gives exactly the unproduced output. Removing the next root emits its first label; replacing that tree by its children preserves the remainder of that preorder traversal. Adding children at the front realizes an explicit-stack depth-first tree traversal; placing them at the end would give a different visiting order. The first states for the source example have roots
[1], [2,5,6], [3,4,5,6], [4,5,6], [5,6], [6], [7], [].The most general polymorphic type is
unfold :: (s -> Bool) -> (s -> a) -> (s -> s) -> s -> [a]Using the specified list unfold, the definitions are
map f = unfold null (f . head) tail
iterate f = unfold (const False) id fFor
map, the current state is the remaining input list. null prevents head and tail from being evaluated on an empty list. For iterate, the stop predicate is always false and the output is the current state itself. Its transition is f, so the original seed is emitted before the first application of f.The list unfold operation has the following type signature and definition:The seed is the unprocessed suffix of a list. An empty seed terminates the unfold; otherwise
unfoldr :: (s -> Maybe (a,s)) -> s -> [a]
unfoldr step s = case step s of
Nothing -> []
Just (x,s') -> x : unfoldr step s'
groupUnfold :: Eq a => [a] -> [[a]]
groupUnfold = unfoldr next
where
next [] = Nothing
next (x:xs) = Just (x:ys,zs)
where (ys,zs) = span (== x) xsnext returns the first maximal run and precisely the seed for the next call. Substituting the definition of unfoldr recovers part (c)'s equations, proving correctness. The Just pair and first group constructor can be produced before span finishes, retaining the same lazy evaluation behaviour.