来自Writer和State monads的GHC 8.0.1错误

时间:2016-12-10 10:58:26

标签: haskell monads

我对Haskell相对较新,并尝试使用monadic组合移植在Kernighan和Ritchie中演示的UNIX wc程序,如Jeremy Gibbons和Bruno C.的The Essence of the Iterator Pattern所示。 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.

在任何一种情况下,我都无法理解我在做什么。任何帮助将不胜感激。谢谢!

1 个答案:

答案 0 :(得分:3)

要使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)

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

另一种类型错误是GHC告诉您,您无法使用Writer操作撰写State操作。您有wcm :: State (Integer, Bool) Stringccm :: Writer Integer String。浏览您的代码,看起来您只是使用州的Integer组件来添加内容(我猜测它是否意味着参与运行总计)以及Writer位?),所以我会考虑使用State monad transformer 版本:

wcm :: StateT Bool (Writer Integer) String

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

如果您还不习惯使用monad变换器,另一种选择是在State (Integer, Bool) monad中写下所有内容。