如何玩Haskell中的Control.Monad.Writer?

我是function编程的新手,最近在学习你一个Haskell的时候学习,但是当我阅读本章的时候 ,我被下面的程序困住了:

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) 

我把这些行保存在一个.hs文件中,但没有把它导入到我的ghci中,

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

我用“:info”命令检查了这个types:

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

从我的angular度来看,这应该是像“新型作家wa …”,所以我很困惑如何提供数据构造函数,并得到一个作家。

我想这可能是一个版本相关的问题,我的ghci版本是7.4.1

包的Control.Monad.Writer不会导出数据构造函数Writer 。 我想这个LYAH写的时候是不一样的。

在ghci中使用MonadWritertypes类

相反,您使用编写器function创build编写器。 例如,在一个ghci会话中,我可以做

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

现在logNumber是一个创build作家的函数。 我可以问它的types:

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

这告诉我,推断的types不是返回特定作者的函数,而是实现MonadWritertypes类的任何东西。 我现在可以使用它:

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

(input实际上全部在一行上)。 在这里我已经指定了multWithLogtypes为Writer [String] Int 。 现在我可以运行它了:

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

而且你看到我们logging了所有的中间操作。

为什么代码是这样写的?

为什么要麻烦创buildMonadWritertypes的类呢? 原因是与monad变压器有关。 正如你正确认识到的那样,实现Writer的最简单的方法是在一对之上作为一个新types的包装:

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

您可以为此声明monad实例,然后编写该函数

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

它只是logging其input。 现在假设你想要一个具有日志logging能力的monad,但也做了其他的事情,比如说它也可以从环境中读取。 你会实现这个

 type RW rwa = ReaderT r (Writer wa) 

现在,因为作者在ReaderT monad变换器中,所以如果你想logging输出,你不能使用tell w (因为它只能用解包的作者操作),但是你必须使用lift $ tell wlift $ tell w tell函数通过ReaderT以便它可以访问内部作家monad。 如果你想要两层变换器(比如你想添加error handling),那么你需要使用lift $ lift $ tell w 。 这很快变得笨拙。

相反,通过定义一个types类,我们可以将作者周围的任何monad变换器封装到作者本身的一个实例中。 例如,

 instance (Monoid w, MonadWriter wm) => MonadWriter w (ReaderT rm) 

也就是说,如果w是monoid, mMonadWriter w ,则ReaderT rm也是MonadWriter w 。 这意味着我们可以直接在转换后的monad上使用tell函数,而不必费力地通过monad变换器来显式提取它。

一个名为“writer”的函数可用来代替“Writer”构造函数。 更改:

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

至:

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