Eu sou novo em programação funcional e recentemente aprendi em Learn You a Haskell , mas quando li este capítulo , fiquei preso no programa abaixo:
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)
Salvei essas linhas em um arquivo .hs, mas não consegui importá-lo para o meu ghci que reclamava:
more1.hs:4:15:
Not in scope: data constructor `Writer'
Perhaps you meant `WriterT' (imported from Control.Monad.Writer)
Failed, modules loaded: none.
Eu examinei o tipo pelo comando ": info":
Prelude Control.Monad.Writer> :info Writer
type Writer w = WriterT w Data.Functor.Identity.Identity
-- Defined in `Control.Monad.Trans.Writer.Lazy'
Do meu ponto de vista, isso deveria ser algo como "newtype Writer wa ...", então estou confuso sobre como alimentar o construtor de dados e obter um Writer.
Acho que pode ser um problema relacionado à versão e minha versão ghci é 7.4.1
Respostas:
O pacote
Control.Monad.Writer
não exporta o construtor de dadosWriter
. Eu acho que isso era diferente quando LYAH foi escrito.Usando a typeclass MonadWriter em ghci
Em vez disso, você cria escritores usando a
writer
função. Por exemplo, em uma sessão ghci eu posso fazerAgora
logNumber
é uma função que cria escritores. Posso pedir seu tipo:O que me diz que o tipo inferido não é uma função que retorna um escritor específico , mas sim qualquer coisa que implemente a
MonadWriter
classe de tipo. Agora posso usar:(Na verdade, a entrada foi inserida em uma linha). Aqui, especifiquei o tipo de
multWithLog
serWriter [String] Int
. Agora posso executá-lo:E você vê que registramos todas as operações intermediárias.
Por que o código é escrito assim?
Por que se preocupar em criar a
MonadWriter
classe de tipo? A razão tem a ver com transformadores de mônadas. Como você percebeu corretamente, a maneira mais simples de implementarWriter
é como um wrapper de novo tipo em cima de um par:Você pode declarar uma instância de mônada para isso e, em seguida, escrever a função
que simplesmente registra sua entrada. Agora, suponha que você queira uma mônada que tenha recursos de registro, mas também faça outra coisa - digamos que ela possa ler de um ambiente também. Você implementaria isso como
Agora, como o gravador está dentro do
ReaderT
transformador de mônada, se você deseja registrar a saída, não pode usartell w
(porque isso só funciona com gravadores não embalados), mas tem que usarlift $ tell w
, o que "levanta" atell
função através doReaderT
para que possa acessar o mônada interior do escritor. Se você quiser dois transformadores de camadas (digamos que você também queira adicionar tratamento de erros), será necessário usá-loslift $ lift $ tell w
. Isso rapidamente se torna difícil.Em vez disso, ao definir uma classe de tipo, podemos transformar qualquer transformador de mônada que envolve um escritor em uma instância do próprio escritor. Por exemplo,
ou seja, se
w
for um monóide em
for aMonadWriter w
, entãoReaderT r m
também será aMonadWriter w
. Isso significa que podemos usar atell
função diretamente na mônada transformada, sem ter que nos preocupar em levantá-la explicitamente através do transformador da mônada.fonte
mtl
passagem da versão principal 1. * para a 2. *, logo depois que LYAH e RWH foram escritos. Momento extremamente infeliz que levou e leva a muita confusão entre os iniciantes.Control.Monad.Trans.Writer
. Além disso, o tipo delogNumber
élogNumber :: (Show a, Monad m) => a -> WriterT [[Char]] m a
para mim.mtl
biblioteca instalada (o que provavelmente significa que você tem uma instalação básica do GHC, como minGHC, ao invés da Plataforma Haskell). Em um prompt de comando, executecabal update
ecabal install mtl
tente novamente.writer
é usado no lugar doWriter
último, o ctor de valor, não é exportado pelo módulo, enquanto o primeiro é, e pode ser usado para criar o mesmo valor que você criaria com o ctor, mas o faz não permitir correspondência de padrões.Uma função chamada "escritor" é disponibilizada no lugar de um construtor "Escritor". Mudança:
logNumber x = Writer (x, ["Got number: " ++ show x])
para:
logNumber x = writer (x, ["Got number: " ++ show x])
fonte
Recebi uma mensagem semelhante ao tentar o LYAH "For a few Monads More" usando o editor Haskell online em repl.it
Eu mudei a importação de:
para:
Então, meu código agora funciona assim (com inspiração no Blog Haskell de Kwang ):
O código pode ser executado aqui
fonte