Entonces, la mejor manera de entenderlo es hacerlo. A continuación hay una implementación de foldlM
uso en foldl
lugar de foldr
. Es un buen ejercicio, pruébalo y luego ve a la solución que sugeriría. El ejemplo explica todo el razonamiento que he hecho para lograrlo, que podría ser diferente al suyo y podría ser parcial porque ya sabía sobre el uso de un acumulador de funciones.
Paso 1 : tratemos de escribir foldlM
en términos defoldl
-- 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
Aquí te das cuenta de que f'
es puro y necesitarías extraer el resultado de f
escribir coincidencia. La única forma de 'extraer' un valor monádico es con el >>=
operador, pero dicho operador debe ajustarse inmediatamente después de su uso.
Como conclusión: cada vez que termines me gustaría desenvolver completamente esta mónada , solo ríndete. No es el camino correcto
Paso 2 : Intentemos escribir foldlM
en términos de, foldl
pero primero, usarlo []
como plegable, ya que es fácil de combinar patrones (es decir, en realidad no necesitamos usar 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, eso fue fácil. Vamos a comparar la definición con la foldl
definición habitual para listas
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
¡¡Frio!! Son más o menos lo mismo. El caso trivial es exactamente lo mismo. El caso recursivo es un poco diferente de bits, desea escribir algo más como: foldlM' f (f z0 x) xs
. Pero no se compila como en el paso 1, por lo que puede pensar que está bien, no quiero aplicar f
, solo para mantener dicho cálculo y componerlo >>=
. Me gustaría escribir algo más como foldlM' f (f z0 x >>=) xs
si tuviera sentido ...
Paso 3 Date cuenta de que lo que quieres acumular es una composición de funciones y no un resultado. ( Aquí probablemente soy parcial por el hecho de que ya lo sabía porque lo has publicado ).
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.
Por el tipo initFunc
y el uso de nuestro conocimiento del paso 2 (la definición recursiva) podemos deducir eso initFunc = return
. La definición de f'
se puede completar sabiendo que f'
debe usar f
y >>=
.
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
Como puede ver, no es tan difícil hacerlo. Necesita práctica, pero no soy un desarrollador profesional de haskell y podría hacerlo yo mismo. Es una cuestión de práctica.