2010-02-25 4 views
11

ich so etwas wie dies schrieb:die Reihenfolge der Argumente Manipulieren von Konstrukteuren geben

instance Functor (Either e) where 

    fmap _ (Left a) = Left a 

    fmap f (Right b) = Right (f b) 

Wie kann ich das gleiche tun, wenn ich fmap will nur den Wert ändern, wenn es Left?

Ich meine, welche Syntax verwende ich, um anzuzeigen, dass ich den Typ Either _ b anstelle von Either a _ verwende?

+1

Related: http://stackoverflow.com/questions/1827645 Kurz gesagt, es ist nicht möglich in Haskell, wie es jetzt steht. – ephemient

+0

Nebenbei ist der Titel dieser Frage etwas unklar, aber ich bin mir nicht sicher, was besser wäre. –

+0

Ich schlug einen präziseren Titel vor. –

Antwort

7

Ich glaube nicht, dass es einen Weg gibt, dies direkt zu tun, leider. Mit einer Funktion können Sie flip verwenden, um das zweite Argument teilweise anzuwenden, aber das funktioniert nicht mit Typkonstruktoren wie Either.

Die einfachste Sache wahrscheinlich wickelt es in einem newtype:

newtype Mirror b a = Mirrored (Either a b) 

instance Functor (Mirror e) where 
    fmap _ (Mirrored (Right a)) = Mirrored $ Right a 
    fmap f (Mirrored (Left b)) = Mirrored $ Left (f b) 

Wrapping mit newtype ist auch der normale Weg, mehrere Instanzen für eine einzige Art, wie Sum und Product sind Instanzen von Monoid für numerische erstellen Arten. Andernfalls können Sie nur eine Instanz pro Typ haben.

Zusätzlich je nachdem, was Sie tun wollen, ist eine weitere Option Functor und definieren Sie Ihre eigene Art Klasse wie folgt zu ignorieren:

class Bifunctor f where 
    bimap :: (a -> c) -> (b -> d) -> f a b -> f c d 

instance Bifunctor Either where 
    bimap f _ (Left a) = Left $ f a 
    bimap _ g (Right b) = Right $ g b 

instance Bifunctor (,) where 
    bimap f g (a, b) = (f a, g b) 

Offensichtlich, dass die Klasse doppelt so viel Spaß als regelmäßige ist Functor. Natürlich können Sie eine Monad Instanz nicht sehr einfach daraus machen.

+1

Was ist mit 'newtype Flip t a b = Flip (t b a)' und dann 'instance Functor (Flip Entweder e) '? –

+0

Es funktioniert nicht: "Alle Instanztypen müssen die Form (T a1 ... an) haben, wobei a1 ... an den Typ * variables *" – mik01aj

+0

@ m01: Es funktioniert, erfordert aber die Aktivierung einer GHC-Spracherweiterung . In der Praxis würde ich entweder (kein Wortspiel beabsichtigt) es so machen, wie es Norman Ramsey vorschlägt oder die 'Bifunkter'-Klasse benutzt, wie in meiner Antwort. Der überspezialisierte Typ "Mirror" sollte hauptsächlich die Idee veranschaulichen. –

3

Sie benötigen im Wesentlichen einen 'Flip'-Kombinator für Typen. Ein Wrapper des neuen Typs, der die Reihenfolge umkehrt, sollte funktionieren, wie camccann sagt. Beachten Sie, dass Sie kein "Typ" -Synonym verwenden können, da sie möglicherweise nicht teilweise angewendet werden.

4

Sie können die gesuchte Instanz nicht direkt erstellen.

Damit Typinferenz- und Typklassen funktionieren, gibt es eine gewisse Positionsabweichung bei der Sortierung von Argumenten in den Typen. Es wurde gezeigt, dass, wenn wir willkürliche Neuordnung der Argumente bei der Instanziierung von Typklassen zulassen, diese Art von Inferenz unhandlich wird.

Sie könnten eine Bifunctor Klasse verwenden, die beide Argumente separat abbilden kann.

class Bifunctor f where 
    bimap :: (a -> b) -> (c -> d) -> f a c -> f b d 
    first :: (a -> b) -> f a c -> f b c 
    second :: (c -> d) -> f a c -> f a d 

    first f = bimap f id 
    second = bimap id 

instance Bifunctor Either where 
    bimap f _ (Left a) = Left (f a) 
    bimap _ g (Right b) = Right (g b) 

instance Bifunctor (,) where 
    bimap f g (a,b) = (f a, g b) 

Oder Sie könnten eine Flip combinator wie verwenden:

newtype Flip f a b = Flip { unFlip :: f b a } 

Generalized Versionen der beiden von ihnen sind in der Kategorie-Extras auf Hackage zur Verfügung. Letzteres enthält sogar eine Instanz für Functor (Flip Either a), weil Either eine Bifunctor ist. (Ich sollte wahrscheinlich beheben, dass nur eine PFunctor erforderlich)

Schließlich ist die Reihenfolge der Argumente in einem Typkonstruktor wichtig bei der Bestimmung, welche Klassen Sie instanziieren können. Möglicherweise müssen Sie newtype wrappers (wie Flip oben) verwenden, um die Argumente zu platzieren, wo sie sein müssen, um eine Instanz einer anderen Typklasse zu erstellen. Dies ist der Preis, den wir für die Ableitung von Typklassenbeschränkungen zahlen.

Verwandte Themen