you monad learn example haskell monads

learn - ¿Cómo se juega con Control.Monad.Writer en haskell?



monad example (3)

El paquete Control.Monad.Writer no exporta el Writer constructor de datos. Supongo que esto fue diferente cuando LYAH fue escrito.

Usando la clase de tipo MonadWriter en ghci

En cambio, crea escritores que usan la función de escritura. Por ejemplo, en una sesión de ghci puedo hacer

ghci> import Control.Monad.Writer ghci> let logNumber x = writer (x, ["Got number: " ++ show x])

Ahora logNumber es una función que crea escritores. Puedo preguntar por su tipo:

ghci> :t logNumber logNumber :: (Show a, MonadWriter [String] m) => a -> m a

Lo que me dice que el tipo inferido no es una función que devuelve un escritor en particular , sino algo que implementa la clase de tipo MonadWriter . Ahora puedo usarlo:

ghci> let multWithLog = do { a <- logNumber 3; b <- logNumber 5; return (a*b) } :: Writer [String] Int

(Entrada ingresada en realidad en una sola línea). Aquí he especificado el tipo de multWithLog para ser Writer [String] Int . Ahora puedo ejecutarlo:

ghci> runWriter multWithLog (15, ["Got number: 3","Got number: 5"])

Y verá que registramos todas las operaciones intermedias.

¿Por qué el código está escrito así?

¿Por qué molestarse en crear la clase de tipo MonadWriter ? La razón es hacer con los transformadores de mónada. Como se dio cuenta correctamente, la forma más sencilla de implementar Writer es como un contenedor de tipo nuevo sobre un par:

newtype Writer w a = Writer { runWriter :: (a,w) }

Puede declarar una instancia de mónada para esto y luego escribir la función

tell :: Monoid w => w -> Writer w ()

que simplemente registra su entrada. Ahora supongamos que quiere una mónada que tenga capacidades de registro, pero también hace otra cosa: decir que también puede leer de un entorno. Implementarías esto como

type RW r w a = ReaderT r (Writer w a)

Ahora, como el escritor está dentro del transformador de mónada ReaderT , si desea registrar la salida, no puede usar tell w (porque eso solo funciona con escritores desenvueltos), pero debe usar lift $ tell w , que "eleva" la función tell a través del ReaderT para que pueda acceder a la mónada del escritor interno. Si quería transformadores de dos capas (supongamos que desea agregar el manejo de errores también), entonces necesitaría usar lift $ lift $ tell w . Esto se vuelve difícil de manejar.

En cambio, al definir una clase de tipo, podemos convertir cualquier envoltura de transformador de mónada alrededor de un escritor en una instancia de escritor. Por ejemplo,

instance (Monoid w, MonadWriter w m) => MonadWriter w (ReaderT r m)

es decir, si w es un monoide y m es un MonadWriter w , entonces ReaderT rm también es un MonadWriter w . Esto significa que podemos usar la función tell directamente en la mónada transformada, sin tener que molestarnos en levantarla explícitamente a través del transformador de mónada.

Soy nuevo en la programación funcional y recientemente aprendí en Learn You a Haskell , pero cuando revisé este capítulo , me quedé atrapado en el siguiente programa:

import Control.Monad.Writer logNumber :: Int -> Writer [String] Int logNumber x = Writer (x, ["Got number: " ++ show x]) multWithLog :: Writer [String] Int multWithLog = do a <- logNumber 3 b <- logNumber 5 return (a*b)

Guardé estas líneas en un archivo .hs pero no pude importarlo a mi ghci que se quejó:

more1.hs:4:15: Not in scope: data constructor `Writer'' Perhaps you meant `WriterT'' (imported from Control.Monad.Writer) Failed, modules loaded: none.

Examiné el tipo con el comando ": info":

Prelude Control.Monad.Writer> :info Writer type Writer w = WriterT w Data.Functor.Identity.Identity -- Defined in `Control.Monad.Trans.Writer.Lazy''

Desde mi punto de vista, se suponía que era algo así como "newtype Writer wa ...", así que estoy confundido acerca de cómo alimentar el constructor de datos y obtener un Writer.

Supongo que podría ser un problema relacionado con la versión y mi versión ghci es 7.4.1


Recibí un mensaje similar al probar el LYAH "Por unas pocas Mónadas más" usando el editor en línea de Haskell en repl.it

Cambié la importación de:

import Control.Monad.Writer

a:

import qualified Control.Monad.Trans.Writer.Lazy as W

Entonces mi código ahora funciona así (con inspiración del blog Haskell de Kwang ):

import Data.Monoid import qualified Control.Monad.Trans.Writer.Lazy as W output :: String -> W.Writer [String] () output x = W.tell [x] gcd'' :: Int -> Int -> W.Writer [String] Int gcd'' a b | b == 0 = do output ("Finished with " ++ show a) return a | otherwise = do output (show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)) gcd'' b (a `mod` b) main :: IO() main = mapM_ putStrLn $ snd $ W.runWriter (gcd'' 8 3)

El código actualmente se puede ejecutar aquí


Una función llamada "escritor" está disponible en lugar de un constructor "Escritor". Cambio:

logNumber x = Writer (x, ["Got number: " ++ show x])

a:

logNumber x = writer (x, ["Got number: " ++ show x])