haskell - 来自 Writer 和 State monad 的 GHC 8.0.1 错误

标签 haskell monads

我对 Haskell 比较陌生,并尝试使用单子(monad)组合移植 Kernighan 和 Ritchie 中演示的 UNIX wc 程序,如 The Essence of the Iterator Pattern 所示。作者:杰里米·吉本斯 (Jeremy Gibbons) 和布鲁诺·C. d. S. Oliveira,编译时遇到一些麻烦。这是我的代码:

import Control.Monad.Writer
import Control.Monad.State
import Data.Char

test :: Bool -> Integer
test b = if b then 1 else 0

ccmBody :: Char -> Writer Integer Char
ccmBody c = do
  tell 1
  return c

ccm :: String -> Writer Integer String
ccm = mapM ccmBody

lcmBody :: Char -> Writer Integer Char
lcmBody c = do
  tell(test(c == '\n'))
  return c

lcm' :: String -> Writer Integer String
lcm' = mapM lcmBody

wcmBody :: Char -> State (Integer, Bool) Char
wcmBody c = let s = not (isSpace c) in do
              (n,w) <- get
              put (n + test(not (w || s)), s)
              return c

wcm :: String -> State (Integer, Bool) String
wcm = mapM wcmBody

clwcm = ccm >=> lcm' >=> wcm

编译器错误:

wordcount.hs:10:3: error: …
    • No instance for (Monoid Integer) arising from a do statement
    • In a stmt of a 'do' block: tell 1
       In the expression:
        do { tell 1;
             return c }
      In an equation for ‘ccmBody’:
          ccmBody c
            = do { tell 1;
                   return c }
wordcount.hs:33:26: error: …
    • Couldn't match type ‘StateT
                             (Integer, Bool) Data.Functor.Identity.Identity’
                     with ‘WriterT Integer Data.Functor.Identity.Identity’
      Expected type: String
                     -> WriterT Integer Data.Functor.Identity.Identity String
        Actual type: String -> State (Integer, Bool) String
    • In the second argument of ‘(>=>)’, namely ‘wcm’
      In the second argument of ‘(>=>)’, namely ‘lcm' >=> wcm’
      In the expression: ccm >=> lcm' >=> wcm
Compilation failed.

无论哪种情况,我都无法理解我在这里做错了什么。任何帮助将不胜感激。谢谢!

最佳答案

要使 Writer monad 正常工作,您正在编写的对象必须是 Monoid 的实例。 tell 使用 mappend 将其参数添加到运行日志中。

但是,对于整数,Monoid 实例至少有两个不错的选择。其中之一是加法,以0为单位元素:

instance Monoid Int where
    mempty = 0
    mappend = (+)

另一个是与1相乘:

instance Monoid Int where
    mempty = 1
    mappend = (*)

Haskell 设计者应该如何在这两个选项之间进行选择?它们同样有效,并且在不同情况下都有用。最后,他们决定不表达意见,让 Int 没有自己的 Monoid 实例,而是提供两个 newtype 来允许您选择您想要的实例。

newtype Sum n = Sum { getSum :: n }
instance Num n => Monoid (Sum n) where
    mempty = Sum 0
    Sum x `mappend` Sum y = Sum (x + y)

newtype Product n = Product { getProduct :: n }
instance Num n => Monoid (Product n) where
    mempty = Product 1
    Product x `mappend` Product y = Product (x * y)

找到这两个newtype in the Data.Monoid module 。我猜在你的情况下,你想将 Writer monad 中的数字相加,所以你需要做的就是将所有类型签名从 Writer Integer 更改为Writer(整数求和)


另一种类型错误是 GHC 告诉您无法将 Writer 操作与 State 操作组合起来。您有 wcm::State (Integer, Bool) Stringccm::Writer Integer String。快速阅读您的代码,看起来您只使用状态的 Integer 组件来添加内容(我猜它应该与 一起参与运行总计) >Writer 位?),所以我会考虑使用 Statemonad Transformer 版本:

wcm :: StateT Bool (Writer Integer) String

然后使用lift将普通的旧Writer Integer monad带入StateT丰富的上下文中。

如果您还不习惯 monad 转换器,另一个选择是将所有内容写入 State (Integer, Bool) monad。

关于haskell - 来自 Writer 和 State monad 的 GHC 8.0.1 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41074739/

相关文章:

haskell - 选择具有给定签名的函数的实现

haskell - 为什么 AccValidation 不能有 Monad 实例?

haskell - 如何实现一个最优的、纯功能性的、双端优先级队列?

scala - 用于聊天和类似 Twitter 的应用程序的并发程序语言

haskell - 使用镜头库构建复杂的功能

f# - (如何)我可以让这个单子(monad)绑定(bind)尾递归吗?

Haskell 非类型变量参数

haskell - 为什么 (&)::a -> (a -> b) -> b 不被视为 Monad?

haskell - 在运行的 haskell 程序中生成输出

haskell - Monadic QuickCheck 测试不适用于任意 Repa 阵列