2016-07-31 37 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