В Haskell Wiki Рекурсия в монаде есть пример, который, как утверждается, является хвостом-рекурсивным
f 0 acc = return (reverse acc)
f n acc = do
v <- getLine
f (n-1) (v : acc)
В то время как императивная нотация приводит нас к мысли, что она рекурсивна, она не настолько очевидна вообще (по крайней мере для меня). Если мы де сахара do
, получим
f 0 acc = return (reverse acc)
f n acc = getLine >>= \v -> f (n-1) (v : acc)
и переписывание второй строки приводит к
f n acc = (>>=) getLine (\v -> f (n-1) (v : acc))
Итак, мы видим, что f
происходит во втором аргументе >>=
, а не в хвостовой рекурсивной позиции. Нам нужно изучить IO
>>=
, чтобы получить ответ.
Очевидно, что , имеющий рекурсивный вызов в качестве последней строки в блоке do
, не является достаточным условием, чтобы функция была хвосторекурсивной.
Скажем, что монада является хвост-рекурсивной, если каждая рекурсивная функция в этой монаде определяется как
f = do
...
f ...
или эквивалентно
f ... = (...) >>= \x -> f ...
является хвостовым рекурсивным. Мой вопрос:
- Какие монады являются хвостовыми рекурсивными?
- Есть ли какое-то общее правило, которое мы можем использовать, чтобы сразу отличить хвостовые рекурсивные монады?
Обновление: Позвольте мне сделать конкретный встречный пример: монада []
не является хвостовой рекурсивной в соответствии с приведенным выше определением. Если бы это было так, то
f 0 acc = acc
f n acc = do
r <- acc
f (n - 1) (map (r +) acc)
должен быть хвост-рекурсивным. Однако обессоливание второй линии приводит к
f n acc = acc >>= \r -> f (n - 1) (map (r +) acc)
= (flip concatMap) acc (\r -> f (n - 1) (map (r +) acc))
Ясно, что это не хвостовая рекурсия, и IMHO не может быть сделано. Причина в том, что рекурсивный вызов не является окончанием вычисления. Он выполняется несколько раз, и результаты объединяются, чтобы сделать окончательный результат.