I\'m not even sure this is possible in any kind of monad; does it violate monad laws? But it seems like something that should be possible in some kind of construct or other. S
While I think Daniel Díaz' arrow solution is he perfect way to do this, there is sure enough a simpler one (which, I just see, he also indicates in the comments already) provided, as in your example, no data is passed between the different function calls.
Remember that, since Haskell is lazy, functions can do lots of stuff that would require macros in other languages. In particular, it's no problem whatsoever to have a list of IO
actions. (Absolutely safe, too: due to pureness, there's no way these could "go off early" in Haskell!) Then you can simply take the length of this list as the total count, interleave it with printing statements, and be done. All in the core language, don't need TH!
sequenceWithStepCount :: [IO()] -> IO()
sequenceWithStepCount actions = go actions 0
where nTot = length actions
go [] _ = putStrLn "Done!"
go (act:remains) n = do
putStrLn ("Step "++show n++" of "++show nTot)
act
go remains $ succ n
To be used like
do
sequenceWithStepCount [
someOp ()
, someOtherOp ()
, thirdOp ()
]