【问题标题】:GHC 8.0.1 errors from Writer and State monads来自 Writer 和 State monad 的 GHC 8.0.1 错误
【发布时间】:2016-12-10 10:58:25
【问题描述】:

我对 Haskell 比较陌生,并尝试使用单子组合移植在 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.

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

【问题讨论】:

    标签: haskell monads


    【解决方案1】:

    要使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 实例,而是提供两个newtypes 让您选择您想要的实例。

    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)
    

    这两个newtypes 被发现in the Data.Monoid module。我猜在您的情况下,您想将 Writer monad 中的数字相加,因此您需要做的就是将所有类型签名从 Writer Integer 更改为 Writer (Sum Integer)


    另一种类型的错误是 GHC 告诉您不能用 State 操作组合 Writer 操作。你有wcm :: State (Integer, Bool) Stringccm :: Writer Integer String。略读您的代码,看起来您只是使用状态的 Integer 组件来添加内容(我猜它是为了与 Writer 位一起参与运行总数?),所以我会考虑使用Statemonad转换器版本:

    wcm :: StateT Bool (Writer Integer) String
    

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

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

    【讨论】:

    • 确实,State (Integer, Bool) 可能会更好。 Writer 不适用于 Sum Integer 之类的东西。
    • @dfeuer 你能量化一下吗?你说的是懒惰吗?
    • 是的。甚至Control.Monad.Writer.Strict 在“日志”中也很懒惰。您可以实现自己的MonadWriter 实例并使用它,但转换为尾递归形式最终会使其看起来像Statenewtype WriterT' w m a = WriterT' (StateT w m a) deriving (Functor, Applicative, Monad); instance (Monoid w, Monad m) =&gt; MonadWriter w (WriterT' w m) where tell a = WriterT $ modify' (&lt;&gt; a)。我不确定是否支持所有其他效果,但可能。最后,您基本上会回到State 土地,其中有些东西可能很难验证。
    • 谢谢!我决定对所有事情都使用State,因为我认为这是最有意义的。但我仍然不确定我是否正确地编写它们,因为我在尝试使用 GHCi 中的字符串对其进行测试时遇到输入错误:evalState clwcm "this is a test, this is only a test" 产生 Couldn't match type ‘String -&gt; State (Integer, Bool) String’ with ‘StateT [Char] Data.Functor.Identity.Identity a’ Expected type: State [Char] a Actual type: String -&gt; State (Integer, Bool) String
    • 原来我混淆了论文中的代码。它不使用 kleisli 箭头组成单子,而是通过像这样的乘积函子:data Product' m n a = Product { pfst :: m a, psnd :: n a } deriving (Show)prod :: (Functor m, Functor n) =&gt; (a -&gt; m b) -&gt; (a -&gt; n b) -&gt; (a -&gt; Product' m n b)prod f g x = Product' (f x) (g x)。我不确定我是否理解他们的用意,但我不需要他们来模拟 C 版本的结果,它本质上只是返回一个三元组。
    猜你喜欢
    • 2014-09-11
    • 2012-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-07
    • 1970-01-01
    相关资源
    最近更新 更多