gpt4 book ai didi

haskell - 如何在 haskell 中使用 Control.Monad.Writer?

转载 作者:行者123 更新时间:2023-12-03 05:02:00 25 4
gpt4 key购买 nike

我是函数式编程新手,最近在 Learn You a Haskell 学习,但是当我经历this chapter时,我被下面的程序卡住了:

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”命令检查了类型:

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

从我的角度来看,这应该是“newtype Writer w a ...”所以我对如何提供数据构造函数并获取 Writer 感到困惑。

我猜这可能是版本相关的问题,我的ghci版本是7.4.1

最佳答案

包裹Control.Monad.Writer不导出数据构造函数 Writer 。我猜写 LYAH 时情况有所不同。

在 ghci 中使用 MonadWriter 类型类

相反,您可以使用 writer 创建编写器功能。例如,在 ghci session 中我可以这样做

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

现在logNumber是一个创建 writer 的函数。我可以询问它的类型:

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

这告诉我,推断的类型不是返回特定编写器的函数,而是任何实现 MonadWriter 的函数。类型类。我现在可以使用它了:

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

(输入实际上全部输入一行)。这里我指定了 multWithLog 的类型成为Writer [String] Int 。现在我可以运行它了:

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

您会看到我们记录了所有中间操作。

为什么代码要这样写?

为什么要费心创建 MonadWriter到底有没有类型类?原因与 monad 转换器有关。正如您正确认识到的,最简单的实现方法 Writer作为一对之上的新类型包装器:

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

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

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

它只是记录其输入。现在假设您想要一个具有日志记录功能的 monad,但也可以执行其他操作 - 假设它也可以从环境中读取。您可以将其实现为

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

现在因为作者在 ReaderT 内monad 转换器,如果你想记录输出,你不能使用 tell w (因为这只适用于未包装的编写器)但您必须使用 lift $ tell w ,它“提升”了 tell通过ReaderT功能这样它就可以访问内部 writer monad。如果您想要两层变压器(假设您还想添加错误处理),那么您需要使用 lift $ lift $ tell w 。这很快就会变得笨拙。

相反,通过定义类型类,我们可以将编写器周围的任何 monad 转换器包装器转换为编写器本身的实例。例如,

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

也就是说,如果 w是一个幺半群,且 mMonadWriter w ,然后ReaderT r m也是MonadWriter w 。这意味着我们可以使用tell直接在转换后的 monad 上运行,而不必费心通过 monad 转换器显式提升它。

关于haskell - 如何在 haskell 中使用 Control.Monad.Writer?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11684321/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com