Soy nuevo en la programación funcional y recientemente aprendí en Learn You a Haskell , pero cuando revisé este capítulo , me quedé atascado con 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 y 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 por 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 esto 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 de ghci es 7.4.1
Respuestas:
El paquete
Control.Monad.Writer
no exporta el constructor de datosWriter
. Supongo que esto era diferente cuando se escribió LYAH.Usando la clase de tipo MonadWriter en ghci
En su lugar, crea escritores utilizando la
writer
función. Por ejemplo, en una sesión de ghci puedo hacerAhora
logNumber
es una función que crea escritores. Puedo preguntar por su tipo:Lo que me dice que el tipo inferido no es una función que devuelve un escritor en particular , sino cualquier cosa que implemente la
MonadWriter
clase de tipo. Ahora puedo usarlo:(Entrada realmente ingresada todo en una línea). Aquí he especificado el tipo de
multWithLog
serWriter [String] Int
. Ahora puedo ejecutarlo:Y ves que registramos todas las operaciones intermedias.
¿Por qué el código está escrito así?
¿Por qué molestarse en crear la
MonadWriter
clase de tipo? La razón tiene que ver con los transformadores de mónadas. Como se dio cuenta correctamente, la forma más sencilla de implementarWriter
es como un contenedor de tipo nuevo encima de un par:Puede declarar una instancia de mónada para esto y luego escribir la función
que simplemente registra su entrada. Ahora suponga que desea una mónada que tenga capacidades de registro, pero que también haga algo más, digamos que también puede leer desde un entorno. Implementarías esto como
Ahora, debido a que el escritor está dentro del
ReaderT
transformador de mónada, si desea registrar la salida, no puede usartell w
(porque eso solo funciona con escritores no envueltos) pero debe usarlift $ tell w
, lo que "eleva" latell
función a través delReaderT
para que pueda acceder al mónada del escritor interior. Si desea transformadores de dos capas (digamos que también desea agregar control de errores), entonces debe usarlift $ lift $ tell w
. Esto rápidamente se vuelve difícil de manejar.En cambio, al definir una clase de tipo, podemos convertir cualquier envoltorio transformador de mónada alrededor de un escritor en una instancia del escritor mismo. Por ejemplo,
es decir, si
w
es un monoide ym
es aMonadWriter w
, entoncesReaderT r m
también es aMonadWriter w
. Esto significa que podemos usar latell
función directamente en la mónada transformada, sin tener que molestarnos en levantarla explícitamente a través del transformador de mónada.fuente
mtl
pasar de la versión principal 1. * a 2. *, poco después de que se escribieran LYAH y RWH. Momento extremadamente desafortunado que llevó y conduce a mucha confusión entre los principiantes.Control.Monad.Trans.Writer
. Además el tipo delogNumber
eslogNumber :: (Show a, Monad m) => a -> WriterT [[Char]] m a
para mí.mtl
biblioteca instalada (lo que probablemente signifique que tiene una instalación básica de GHC, como minGHC, en lugar de la plataforma Haskell). Desde el símbolo del sistema, ejecutecabal update
ycabal install mtl
vuelva a intentarlo.writer
se usa en lugar deWriter
como el último, el valor ctor, no es exportado por el módulo, mientras que el primero sí, y se puede usar para crear el mismo valor que crearía con el ctor, pero no no permitir la coincidencia de patrones.Una función llamada "escritor" está disponible en lugar de un constructor "Writer". Cambio:
logNumber x = Writer (x, ["Got number: " ++ show x])
a:
logNumber x = writer (x, ["Got number: " ++ show x])
fuente
Recibí un mensaje similar al probar LYAH "For a few Monads More" usando el editor Haskell en línea en repl.it
Cambié la importación de:
a:
Entonces, mi código ahora funciona con este aspecto (inspirado en el blog Haskell de Kwang ):
El código se puede ejecutar actualmente aquí
fuente