Возьмем функцию типа (Monad m) => a -> m a
. Например:
ghci> let f x = Just (x+1)
Я хотел бы иметь возможность применять его любое количество раз. Первое, что я попробовал, это
ghci> let times n f = foldr (>=>) return $ replicate n f
Проблема в том, что он не будет работать для больших n
:
ghci> 3 `times` f $ 1
Just 4
ghci> 1000000 `times` f $ 1
Just *** Exception: stack overflow
Это не работает и по-другому:
ghci> let timesl n f = foldl' (<=<) return $ replicate n f
ghci> 3 `timesl` f $ 1
Just 4
ghci> 1000000 `timesl` f $ 1
Just *** Exception: stack overflow
На самом деле работает оператор строгости ($!)
.
ghci> let timesStrict n f = foldr1 ((>=>) . ($!)) $ replicate n f
ghci> 3 `timesStrict` f $ 1
Just 4
ghci> 10000000 `timesStrict` f $ 1
Just 10000001
Есть ли более красивое или более идиоматичное решение? Или, может быть, более строгий? Я по-прежнему легко получаю переполнение стека, если f
является тяжелой функцией.
UPD: Я обнаружил, что запись times
в точечной форме также не решает проблему составления тяжеловесных монадических действий. Это работает для f x = Just (x+1), но не работает в реальном мире:
times f 0 a = return a
times f i a = (f $! a) >>= times f (i - 1)