我对 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) String
和 ccm::Writer Integer String
。快速阅读您的代码,看起来您只使用状态的 Integer
组件来添加内容(我猜它应该与 一起参与运行总计) >Writer
位?),所以我会考虑使用 State
的 monad 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/