Quindi il modo migliore per capirlo è farlo. Di seguito è riportata un'implementazione foldlM
dell'utilizzo foldl
anziché foldr
. È un buon esercizio, provalo e vieni più tardi alla soluzione che suggerirei. L'esempio spiega tutto il ragionamento che ho fatto per realizzarlo, che potrebbe essere diverso dal tuo, e potrebbe essere un errore perché ho già saputo di usare un accumulatore di funzioni.
Passaggio 1 : proviamo a scrivere foldlM
in termini difoldl
-- this doesn't compile because f returning type is (m b) and not just (b)
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f z0 xs
-- So let substitute f by some undefined f'
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
where f' = undefined
-- cool, but f' should use f somehow in order to get the monadic behaviour
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
where f' b a = f somethingIDontkNow
Qui ti rendi conto che f'
è puro e dovresti estrarre il risultato di f
digitare match. L'unico modo per "estrarre" un valore monadico è con l' >>=
operatore, ma tale operatore deve essere avvolto subito dopo l'uso.
Quindi, in conclusione: ogni volta che finisci con mi piacerebbe scartare completamente questa monade , arrenditi e basta. Non è la strada giusta
Passaggio 2 : proviamo a scrivere foldlM
in termini di foldl
ma prima usando []
come pieghevole, dal momento che è facile da abbinare modello (cioè non abbiamo effettivamente bisogno di usare fold
)
-- This is not very hard. It is pretty standard recursion schema. :)
foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 [] = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs
Ok, è stato facile. Confrontiamo la definizione con la normale foldl
definizione per gli elenchi
foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 [] = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs
myfoldl :: (b -> a -> b) -> b -> [a] -> b
myfoldl f z0 [] = z0
myfoldl f z0 (x:xs) = foldl f (f z0 x) xs
Freddo!! sono praticamente uguali. Il caso banale riguarda esattamente la stessa cosa. Il caso ricorsivo è un po 'diverso, vuoi scrivere qualcosa di più simile a: foldlM' f (f z0 x) xs
. Ma non viene compilato come nel passaggio 1, quindi potresti pensare OK, non voglio applicare f
, solo per contenere un tale calcolo e comporlo >>=
. Mi piacerebbe scrivere qualcosa di più come foldlM' f (f z0 x >>=) xs
se avesse senso ...
Passaggio 3 Comprendere che ciò che si desidera accumulare è una composizione di funzioni e non un risultato. ( qui probabilmente sono distorto dal fatto che lo sapevo già perché l'hai pubblicato ).
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' initFunc xs
where initFunc = undefined :: b -> m b
f' = undefined :: (b -> m b) -> a -> (b -> m b) -- This type signature can be deduce because f' should be applied to initFunc and a's from t a.
Dal tipo initFunc
e dall'uso delle nostre conoscenze dal passaggio 2 (la definizione ricorsiva) possiamo dedurlo initFunc = return
. La definizione di f'
può essere completata sapendo che f'
dovrebbe usare f
e >>=
.
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' return xs z0
-- ^^^^^^
-- |- Initial value
where f' b a = \bvalue -> b bvalue >>= \bresult -> f bresult a -- this is equivalent to (b >=> \result -> f result a) which captures the sequence behaviour of the implementation
-- ^ ^^^^^^ ^^^^^^^
-- | | |- This is the result of previous computation
-- | |- f' should return a function b -> m b. Any time you have to return a function, start writing a lambda
-- |- This b is the accumulated value and has type b -> m b
-- Following the types you can write this with enough practise
Come puoi vedere, non è così difficile farlo. Ha bisogno di pratica, ma non sono uno sviluppatore di haskell professionale e potrei farlo da solo, è una questione di pratica