在Haskell中,我想让一个Writer monad成为一个monoid的实例:
instance (Monoid a) => Monoid (Writer (Sum Int) a) where
mempty = return mempty
w1 `mappend` w2 = writer((s++t, s'++t'), Sum (m+n)) where
((s,s'), Sum m) = runWriter w1
((t,t'), Sum n) = runWriter w2
因此,直观地说,如果"数据" Writer monad的类型是一个monoid,我希望能够将整个Writer事物视为一个monoid(由mempty和mappend实现。
但这并不起作用:GHCI编译器说
Illegal instance declaration for `Monoid (Writer (Sum Int) a)'
(All instance types must be of the form (T t1 ... tn)
where T is not a synonym.
Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Monoid (Writer (Sum Int) a)'
我真的不知道什么类型应该是这里的同义词,以及我如何符合编译器的规则。
答案 0 :(得分:3)
每个人都做了这么多工作。编写器monad上的绑定运算符已附加w
s。这也意味着它适用于任意基础monad。
instance (Monoid w, Monoid a, Monad m) => Monoid (WriterT w m a) where
mempty = return mempty
mappend = liftA2 mappend
此时很清楚,即使WriterT
也是多余的,这实际上是一个"实例"这个将军instance
instance (Monoid a, Monad m) => Monoid (m a) where
-- same
但是Haskell的类系统并没有真正允许这样的实例 - 它会匹配从类型构造函数构建的每个monoid。例如,此实例将匹配Sum Int
,然后失败,因为Sum
不是monad。所以你必须为你感兴趣的每个monad单独指定它。
答案 1 :(得分:2)
Writer
是类型别名(link)
type Writer w = WriterT w Identity
所以请改用WriterT ... Identity
。您仍然需要启用FlexibleInstances。
也许这就是你所追求的:
{-# LANGUAGE FlexibleInstances #-}
import Control.Monad.Trans.Writer
import Data.Monoid
import Data.Functor.Identity
instance (Monoid w, Monoid a) => Monoid (WriterT w Identity a) where
mempty = return mempty
m1 `mappend` m2 = writer (a1 <> a2, w1 <> w2)
where
(a1,w1) = runWriter m1
(a2,w2) = runWriter m2
当然,这可以推广到任意Monad而不是Identity。