如何玩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不是返回特定作者的函数,而是实现MonadWriter
types类的任何东西。 我现在可以使用它:
ghci> let multWithLog = do { a <- logNumber 3; b <- logNumber 5; return (a*b) } :: Writer [String] Int
(input实际上全部在一行上)。 在这里我已经指定了multWithLog
types为Writer [String] Int
。 现在我可以运行它了:
ghci> runWriter multWithLog (15, ["Got number: 3","Got number: 5"])
而且你看到我们logging了所有的中间操作。
为什么代码是这样写的?
为什么要麻烦创buildMonadWriter
types的类呢? 原因是与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 w
来lift $ tell w
tell
函数通过ReaderT
以便它可以访问内部作家monad。 如果你想要两层变换器(比如你想添加error handling),那么你需要使用lift $ lift $ tell w
。 这很快变得笨拙。
相反,通过定义一个types类,我们可以将作者周围的任何monad变换器封装到作者本身的一个实例中。 例如,
instance (Monoid w, MonadWriter wm) => MonadWriter w (ReaderT rm)
也就是说,如果w
是monoid, m
是MonadWriter 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])