Prenons une fonction de type (Monad m) => a -> m a
. Par exemple:Composition d'actions monad avec des plis
ghci> let f x = Just (x+1)
J'aimerais pouvoir l'appliquer autant de fois que vous le souhaitez. La première chose que j'ai essayé était
ghci> let times n f = foldr (>=>) return $ replicate n f
Le problème est que cela ne fonctionnera pas pour les grands n
:
ghci> 3 `times` f $ 1
Just 4
ghci> 1000000 `times` f $ 1
Just *** Exception: stack overflow
Il ne fonctionne pas aussi dans l'autre sens:
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
En fait, ce qui fonctionne utilise ($!)
opérateur de rigueur
ghci> let timesStrict n f = foldr1 ((>=>) . ($!)) $ replicate n f
ghci> 3 `timesStrict` f $ 1
Just 4
ghci> 10000000 `timesStrict` f $ 1
Just 10000001
Existe-t-il une solution plus sympa ou plus idiomatique? Ou probablement un plus strict? J'obtiens toujours facilement des débordements de pile si f
est une fonction lourde.
UPD: J'ai trouvé qu'écrire times
sous une forme pointue ne résout pas non plus le problème de composer des actions monadiques lourdes. Cela fonctionne pour f x = Just (x + 1), mais échoue dans le monde réel:
times f 0 a = return a
times f i a = (f $! a) >>= times f (i - 1)
Eh bien, toujours le même problème si vous exécutez plus d'itérations: ghci> iterateM_n 1000000 (Just. (+1)) 3 \ n Just *** Exception: débordement de pile \ n ghci> iterateM_n '1000000 (+) 0 (Just. (+1)) 3 \ n Just *** Exception: dépassement de pile \ n – sastanin
@jetxee Mis à jour! –
J'aime utiliser '-XBangPatterns' au lieu de' seq' :-) Quoi qu'il en soit, si 'f' est strict alors il n'y a pas besoin de' >> =! 'Dans ma réponse. Comme il semble que le 'f' de l'OP ne l'est pas, cela pourrait aider. – ephemient