you monad learn example haskell monads

example - reader monad learn you a haskell



Doblando a través de Maybes en Haskell (5)

En un intento de aprender Haskell, me he encontrado con una situación en la que deseo hacer un pliegue sobre una lista pero mi acumulador es un Tal vez. Sin embargo, la función con la que me estoy replegando toma el valor "extraído" en el Maybe y si uno falla, todos fallan. Tengo una solución que encuentro torpe, pero como conocí a Haskell tan poco como yo, creo que debería haber una mejor manera. Digamos que tenemos el siguiente problema de juguetes: queremos sumar una lista, pero los cuatros por alguna razón son malos, por lo que si intentamos sumar un cuatro en cualquier momento queremos devolver Nada. Mi solución actual es la siguiente:

import Maybe explodingFourSum :: [Int] -> Maybe Int explodingFourSum numberList = foldl explodingFourMonAdd (Just 0) numberList where explodingFourMonAdd = (/x y -> if isNothing x then Nothing else explodingFourAdd (fromJust x) y) explodingFourAdd :: Int -> Int -> Maybe Int explodingFourAdd _ 4 = Nothing explodingFourAdd x y = Just(x + y)

Entonces, básicamente, ¿hay una manera de limpiar o eliminar la lambda en la explodingFourMonAdd de Cuatro Monedas, agregando algún tipo de pliegue de Monada? ¿O de alguna manera pasar por el operador >> = para que el pliegue se comporte como una lista de funciones encadenadas por >> =?


Entonces, básicamente, ¿hay una manera de limpiar o eliminar la lambda en la explosión de Cuatro Monedas, agregando algún tipo de pliegue de Monada?

Yapp. En Control.Monad está la función foldM , que es exactamente lo que desea aquí. Por lo que puede reemplazar su llamada a foldl con foldM explodingFourAdd 0 numberList .


Aquí hay otra posibilidad no mencionada por otras personas. Puedes verificar por separado los cuatros y hacer la suma:

import Control.Monad explodingFourSum xs = guard (all (/=4) xs) >> return (sum xs)

Esa es toda la fuente. Esta solución es hermosa en muchos sentidos: reutiliza una gran cantidad de código ya escrito, y expresa muy bien los dos hechos importantes sobre la función (mientras que las otras soluciones publicadas aquí combinan esos dos hechos).

Por supuesto, también hay al menos una buena razón para no usar esta implementación. Las otras soluciones mencionadas aquí atraviesan la lista de entrada solo una vez; esto interactúa muy bien con el recolector de basura, permitiendo que solo pequeñas porciones de la lista estén en la memoria en un momento dado. Esta solución, por otro lado, atraviesa xs dos veces, lo que evitará que el recolector de basura recopile la lista durante la primera pasada.


Creo que puedes usar foldM

explodingFourSum numberList = foldM explodingFourAdd 0 numberList

Esto le permite deshacerse de la lambda extra y eso (solo 0) al principio.

Por cierto, echa un vistazo a hoogle para buscar funciones para las que no recuerdas el nombre.


Puedes explotar el hecho de que Maybe es una mónada. La sequence :: [ma] -> m [a] funciones sequence :: [ma] -> m [a] tiene el siguiente efecto, si m es Maybe : Si todos los elementos de la lista son Just x para algunos x , el resultado es una lista de todos esos justs. De lo contrario, el resultado es Nothing .

Así que primero decides por todos los elementos, si es un fracaso. Por ejemplo, tome su ejemplo:

foursToNothing :: [Int] -> [Maybe Int] foursToNothing = map go where go 4 = Nothing go x = Just x

A continuación, ejecute la secuencia y fmap el pliegue:

explodingFourSum = fmap (foldl'' (+) 0) . sequence . foursToNothing

Por supuesto que tienes que adaptar esto a tu caso específico.


También puedes resolver tu ejemplo de juguete de esa manera:

import Data.Traversable explodingFour 4 = Nothing explodingFour x = Just x explodingFourSum = fmap sum . traverse explodingFour

Por supuesto, esto funciona solo porque un valor es suficiente para saber cuándo falla el cálculo. Si la condición de falla depende de los valores xey de explodingFourSum , debe usar foldM .

Por cierto: una forma elegante de escribir explodingFour sería

import Control.Monad explodingFour x = mfilter (/=4) (Just x)

Este truco también funciona para explodingFourAdd , pero es menos legible:

explodingFourAdd x y = Just (x+) `ap` mfilter (/=4) (Just y)