@mcc The most educational approach is probably to do it all three ways:
1. normal recursive techniques
2. IO monad imperative program
3. Roll your own monad encapsulating the boilerplate from (1) that hopefully shows that the structure from (2) is not tied to IO, but to accumulating some state in the background and having some kind of unwind opration.
Folks will tell you to use monad transformers or effect frameworks. But I don't think that's nearly as educational as doing (1) and (3).