2016-07-31 40 views
1

在Haskell,我想作一个作家单子幺半群的一个实例:让作家(哈斯克尔)的含半幺群实例

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 

因此,直观地,如果“数据”类型的作家单子是一个独异,我希望能够考虑整个作家事,作为一个独异,以及(如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)' 

我真的没有知道什么类型应该是这里的同义词,以及我如何符合编译器的规则。

+1

不符合规则:按照编译器的建议启用'-XTypeSynonymInstances'来放松它们。 '-XFlexiblesInstances'也可能是必需的。 – user2407038

回答

3

大家都在做这么多工作。作家monad上的绑定操作符已经附加了w。这也意味着它可以用于任意基本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

当然有一个扩展来做到这一点,不是吗? – Bergi

+1

多么尴尬!毕竟,我是那个向'Data.Monoid'添加'Ap',用'newtype Ap fa = Ap(fa)','instance(Applicative f,Monoid a)=> Monoid(Ap fa)'' 。请注意'Monad'是过度杀伤。 – dfeuer

+0

@Bergi,有重叠的实例,但它们是邪恶的。 – dfeuer

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 

当然,这可以推广到任意的单子,而不是身份。

+1

我认为扩展可以通过更普遍地处理'WriterT'来避免。 – dfeuer