In Haskell Wiki's Recursion in a monad there is an example that is claimed to be tail-recursive:
f 0 acc = return (reverse acc)
f n acc = do
v <- getLine
f (n-1) (v : acc)
While the imperative notation leads us to believe that it is tail-recursive, it's not so obvious at all (at least to me). If we de-sugar do
we get
f 0 acc = return (reverse acc)
f n acc = getLine >>= \v -> f (n-1) (v : acc)
and rewriting the second line leads to
f n acc = (>>=) getLine (\v -> f (n-1) (v : acc))
So we see that f
occurs inside the second argument of >>=
, not in a tail-recursive position. We'd need to examine IO
's >>=
to get an answer.
Clearly having the recursive call as the last line in a do
block isn't a sufficient condition a function to be tail-recursive.
Let's say that a monad is tail-recursive iff every recursive function in this monad defined as
f = do
...
f ...
or equivalently
f ... = (...) >>= \x -> f ...
is tail-recursive. My question is:
- What monads are tail-recursive?
- Is there some general rule that we can use to immediately distinguish tail-recursive monads?
Update: Let me make a specific counter-example: The []
monad is not tail-recursive according to the above definition. If it were, then
f 0 acc = acc
f n acc = do
r <- acc
f (n - 1) (map (r +) acc)
would have to be tail-recursive. However, desugaring the second line leads to
f n acc = acc >>= \r -> f (n - 1) (map (r +) acc)
= (flip concatMap) acc (\r -> f (n - 1) (map (r +) acc))
Clearly, this isn't tail-recursive, and IMHO cannot be made. The reason is that the recursive call isn't the end of the computation. It is performed several times and the results are combined to make the final result.
f
call is not used. If you'd rather think of it pragmatically, a function is tail-recursive, if, once you do the last call in it, you can dispose of all the context associated with the function. Also, as far as I know, there isn't anything inherently tail-recursive or not-tail-recursive about any monad. – scvalexf
is tail-recursive according to the criteria stated in Tail recursion? – Petr>>=
and see if the result is tail-recursive? – hammarf
is not tail-recursive. On the other hand, I don't agree with that definition as it seems to exclude calling any other function as the first step of expanding the function. By that definition,f = f $ 1
is not tail-recursive. – scvalex