2016-08-05 12 views
0

ich diese Art Synonym haben:Instanz entweder mit speziellen Typ neu definieren

type ParseResult a = Either [CompilerError] (a, [CompilerWarning]) 

Wo CompilerError und CompilerWarning sind Datentypen.

Jetzt weiß ich, dass entweder Beispiel für Functor und Applicative aber die Instanz für Functor gilt fmap auf dem Tupel (a,[CompilerWarning]), hat möchte ich die Instanz für diese Art Synonym neu zu definieren, so dass fmap gilt auf a nicht die ganze Tupel, die gleiche geht für Applicative.

Wenn ich newtype verwende, muss ich ParseResult überall platzieren und ich habe schon viel Code geschrieben.

Ich bin mir bewusst, ich brauche TypeSynonymInstances aber ich vor dem gleichen Problem in this Frage, von der Frage, ich glaube, ich brauche meine Art Synonym wie folgt zu definieren:

type ParseResult = ... 

Ich brauche ... zu füllen, ich weiß nicht, wie man die richtige Seite der Art * -> * mit Either und der tuple, versuchte ich Either [CompilerError] ((,) [CompilerWarning]), aber das hat zwei Probleme: erstens CompilerWarning ist das erste Element, und ich brauche es die zweite (so dass ich nicht habe Um eine Menge Code zu ändern), bekomme ich diese Nachricht:

• ein weiteres Argument Erwartung zu "(,) [CompilerWarning] einen Typ erwartet, aber '(,) [CompilerWarning]' hat Art '* -> *' • Im zweiten Argument von‚Entweder ‘, nämlich '(,) [CompilerWarning] im Typ 'Entweder [Compiler] ((,) [CompilerWarning])' In der Typdeklaration für 'parseResult'

Was ist der beste ist, am wenigsten teure Lösung für dieses Problem?

+2

Make 'ParseResult' eine' newtype' anstelle eines Typ Alias. Auf diese Weise können Sie eigene Instanzen definieren, ohne mit den bereits definierten Definitionen für "Entweder" zu kollidieren. –

+0

Wenn Sie Ihren Datentyp in 'Entweder [CompileError] ([CompilerWarning], a)' ändern können, können Sie einfach zweimal "fmap": '(fmap. Fmap) (+1) (Right ([], 1)) 'ergibt 'Richtig ([], 2)'. – chepner

+0

Ich verstehe nicht wirklich, wie TypeSynonymInstances so viel Verwirrung verursacht. Es ersetzt lediglich ein Typ-Synonym in einem Instanzkopf mit seiner Erweiterung. Sie können das auch selbst tun, so dass Sie TypeSynonymInstances nie brauchen können. –

Antwort

4

Sie können beide ausnutzen Either und (,)bifunctors, nicht nur functors zu sein. Dies bedeutet, dass Sie second . first anstelle von fmap verwenden, um eine Funktion auf den Wert des Typs a anzuwenden.

> import Data.Bifunctor 
> (second . first) (+1) (Right (1, [])) 
Right (2, []) 
> (second . first) (+1) (Left ["oops"]) 
Left ["oops"] 

first f (x, y) entspricht (f x, y).

second f (Right x) entspricht Right f x, während second f (Left y) entspricht Left y.

sie zusammen Einlochen, können Sie das

(second . first) (+1) (Right (1, [])) == second (first (+1)) (Right (1, [])) 
             == Right $ first (+1) (1, []) 
             == Right ((+1) 1, []) 
             == Right (2, []) 

Wenn Sie einen Left stattdessen haben sehen, passiert nichts.

(second . first) (+1) (Left ["oops"]) == second (first (+1)) (Left ["oops"]) 
             == Left ["oops"] 

Da fmap ist die gleiche wie second für Either, dies bedeutet, dass Sie noch fmap verwenden können. Sie müssen die Funktion nur mit first umbrechen, bevor Sie sie verwenden.

(second . first) f == second (first f) 
        == fmap (first f) 

So

> import Data.Bifunctor 
> fmap (first (+1)) (Right (1, [])) 
Right (2, []) 
> fmap (first (+1)) (Left ["oops"]) 
Left ["oops"] 
5

Sie können vorhandene Instanzen nicht neu zu definieren (und es wäre, wenn Sie könnten schrecklich sein).

Ihre Optionen sind:

  • Machen ParseResult ein echter Typ, entweder mit newtype oder so etwas wie

    data ParseResult a = Failure [CompilerError] | Success a [CompilerWarning] 
    

    und definieren Instanzen dafür.

  • nicht mit Typklassen Kümmern Sie sich, und nur Funktionen definieren wie

    mapParseResult :: (a -> b) -> ParseResult a -> ParseResult b 
    
Verwandte Themen