2016-07-31 13 views
1

in Haskell, ich möchte ein Schriftsteller Monade eine Instanz eines Monoid machen:Monoid Instanz von Writer (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 

also intuitiv, wenn die „Daten“ Typ des Writer Monade ein Monoid, möchte ich auch die ganze Writer etwas wie ein Monoid der Lage sein, zu prüfen (wie durch mempty und mappend implementiert

Dies funktioniert nicht, aber:. Der GHCI Compiler sagt

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)' 

und ich wirklich nicht weiß, welcher Typ hier ein Synonym sein soll und wie ich den Regeln des Compilers entsprechen kann.

+1

Entsprechen Sie nicht den Regeln: entspannen Sie sie, indem Sie '-XTypeSynonymInstances' aktivieren, wie der Compiler vorschlägt. "-XFlexiblesInstances" kann ebenfalls erforderlich sein. – user2407038

Antwort

3

Jeder macht so viel Arbeit. Der Bindeoperator einer Schreiber-Monade hängt bereits die w s an. Dies bedeutet auch, dass es für eine beliebige Basismonade funktioniert.

instance (Monoid w, Monoid a, Monad m) => Monoid (WriterT w m a) where 
    mempty = return mempty 
    mappend = liftA2 mappend 

An diesem Punkt ist es klar, dass auch WriterT überflüssig ist, und das ist eigentlich eine „Instanz“ dieser allgemeinen instance

instance (Monoid a, Monad m) => Monoid (m a) where 
    -- same 

aber Haskells Klassensystem ermöglicht nicht wirklich Instanzen wie diese - - Es würde jedem Monoid entsprechen, das aus einem Typkonstruktor erstellt wurde. Zum Beispiel würde diese Instanz mit Sum Int übereinstimmen und dann fehlschlagen, weil Sum keine Monade ist. Sie müssen es separat für jede Monade angeben, die Sie interessiert.

+1

Sicherlich gibt es eine Erweiterung, um das zu tun, nicht wahr? – Bergi

+1

Wie peinlich! Schließlich habe ich vorgeschlagen, 'Ap' zu' Data.Monoid' hinzuzufügen, mit 'newtype Ap fa = Ap (fa)', 'Beispiel (Applicativ f, Monoid a) => Monoid (Ap fa)' . Beachte, dass "Monad" übertrieben ist. – dfeuer

+0

@Bergi, es gibt überlappende Instanzen, aber sie sind böse. – dfeuer

2

Writer ist ein Typ alias (link)

type Writer w = WriterT w Identity 

so WriterT ... Identity stattdessen verwenden. Sie müssen FlexibleInstances weiterhin aktivieren.

Vielleicht ist das, was Sie nach:

{-# 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 

Natürlich ist dies auf eine beliebige Monad verallgemeinert werden könnten anstelle der Identität.

+1

Ich denke, die Erweiterungen können vermieden werden, indem man sich mit 'WriterT' allgemeiner befasst. – dfeuer