type の関数を考えてみましょう(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
ても、重いモナドアクションを作成する問題を解決できないことがわかりました。これは fx = Just (x+1) で機能しますが、現実の世界では失敗します:
times f 0 a = return a
times f i a = (f $! a) >>= times f (i - 1)