Portanto, a melhor maneira de entender é fazendo isso. Abaixo há uma implementação de foldlM
usar em foldl
vez de foldr
. É um bom exercício, tente e chegue mais tarde à solução que eu sugiro. O exemplo explica todo o raciocínio que eu fiz para alcançá-lo, que pode ser diferente do seu e pode ser tendencioso, porque eu já sabia sobre o uso de um acumulador de funções.
Etapa 1 : vamos tentar escrever foldlM
em termos 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
Aqui você percebe que isso f'
é puro e precisa extrair o resultado f
para digitar match. A única maneira de 'extrair' um valor monádico é com o >>=
operador, mas esse operador precisa ser quebrado logo após ser usado.
Então, como conclusão: toda vez que você terminar, eu gostaria de desembrulhar completamente essa mônada , desista. Não é o caminho certo
Etapa 2 : vamos tentar escrever foldlM
em termos de, foldl
mas primeiro usar []
como dobrável, já que é fácil padronizar a correspondência (ou seja, na verdade, não precisamos 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, isso foi fácil. Vamos comparar a definição com a foldl
definição usual 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
Legal!! eles são praticamente iguais. O caso trivial é exatamente a mesma coisa. O caso recursivo é um pouco diferente, você gostaria de escrever algo mais parecido com: foldlM' f (f z0 x) xs
. Mas não é compilado como na etapa 1, então você pode pensar OK, não quero aplicar f
, apenas para manter tal cálculo e compor com ele >>=
. Eu gostaria de escrever algo mais como foldlM' f (f z0 x >>=) xs
se tivesse sentido ...
Etapa 3 Perceba que o que você deseja acumular é uma composição de função e não um resultado. ( aqui estou provavelmente tendencioso pelo fato de já saber porque você o postou ).
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.
Pelo tipo initFunc
e uso de nosso conhecimento da etapa 2 (a definição recursiva), podemos deduzir isso initFunc = return
. A definição de f'
pode ser concluída sabendo que f'
deve usar 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
Como você pode ver, não é tão difícil fazê-lo. Precisa de prática, mas não sou um desenvolvedor profissional de haskell e poderia fazer isso sozinho, é uma questão de prática