44 Stimmen

Verstehen, wie Either eine Instanz von Functor ist

In meiner Freizeit lerne ich gerade Haskell, daher ist dies eine Anfängerfrage.

In meiner Lektüre bin ich auf ein Beispiel gestoßen, das zeigt, wie Either a wird zu einer Instanz von Functor :

instance Functor (Either a) where
    fmap f (Right x) = Right (f x)
    fmap f (Left x) = Left x

Nun versuche ich zu verstehen, warum die Implementierungskarten im Falle einer Right Wert-Konstruktor, aber nicht im Fall eines Left ?

Hier ist mein Verständnis:

Lassen Sie mich zunächst den obigen Fall wie folgt umschreiben

instance Functor (Either a) where
    fmap g (Right x) = Right (g x)
    fmap g (Left x) = Left x

Jetzt:

  1. Ich weiß, dass fmap :: (c -> d) -> f c -> f d

  2. wenn wir ersetzen f con Either a erhalten wir fmap :: (c -> d) -> Either a c -> Either a d

  3. die Art der Right (g x) est Either a (g x) und die Art der g x est d so haben wir, dass der Typ von Right (g x) est Either a d was wir erwarten von fmap (siehe 2. oben)

  4. Nun, wenn wir uns die Left (g x) können wir mit der gleichen Argumentation sagen, dass sein Typ Either (g x) b d.h. Either d b was nicht das ist, was wir von fmap (siehe 2. oben): die d sollte der zweite Parameter sein, nicht der erste! Wir können also nicht über Left .

Ist meine Argumentation richtig?

24voto

fuz Punkte 82245

Das ist richtig. Es gibt aber noch einen anderen, sehr wichtigen Grund für dieses Verhalten: Sie können sich vorstellen Either a b als eine Berechnung, die erfolgreich sein und Folgendes zurückgeben kann b oder schlägt mit einer Fehlermeldung fehl a . (So funktioniert auch die Monadeninstanz). Es ist also nur natürlich, dass die Funktorinstanz nicht mit der Left Werte, da Sie die Berechnung abbilden wollen, wenn sie fehlschlägt, gibt es nichts zu manipulieren.

14voto

applicative Punkte 7943

Ihr Bericht ist natürlich richtig. Vielleicht ist der Grund, warum wir mit solchen Instanzen Schwierigkeiten haben, der, dass wir wirklich unendlich viele Funktorinstanzen auf einmal definieren - eine für jede mögliche Left Typ. Eine Functor-Instanz ist jedoch eine systematische Methode, um mit den unendlich vielen Typen im System zu operieren. Wir definieren also unendlich viele Möglichkeiten, systematisch mit den unendlich vielen Typen im System zu arbeiten. Die Instanz beinhaltet Allgemeinheit in zweierlei Hinsicht.

Aber wenn man es schrittweise angeht, ist es vielleicht gar nicht so seltsam. Die erste dieser Arten ist eine langatmige Version von Maybe unter Verwendung des Einheitentyps () und sein einziger rechtmäßiger Wert () :

data MightBe b     = Nope ()    | Yep b
data UnlessError b = Bad String | Good b
data ElseInt b     = Else Int   | Value b

Hier könnten wir müde werden und eine Abstraktion machen:

data Unless a b    = Mere a     | Genuine b

Jetzt machen wir unsere Functor-Instanzen, ohne Probleme, wobei die erste sehr ähnlich aussieht wie die Instanz für Maybe :

instance Functor MightBe where
  fmap f (Nope ()) = Nope ()   -- compare with Nothing
  fmap f (Yep x)   = Yep (f x) -- compare with Just (f x)

instance Functor UnlessError where
  fmap f (Bad str) = Bad str   -- a more informative Nothing
  fmap f (Good x)  = Good (f x)

instance Functor ElseInt where
  fmap f (Else n) = Else n 
  fmap f (Value b) = Value (f b)

Aber, noch einmal, warum sich die Mühe machen, lassen Sie uns die Abstraktion machen:

instance Functor (Unless a) where
  fmap f (Mere a) = Mere a
  fmap f (Genuine x) = Genuine (f x)

Le site Mere a Begriffe nicht berührt werden, da die () , String y Int Die Werte wurden nicht angetastet.

5voto

Petr Punkte 61399

Wie bereits von anderen erwähnt, Either Typ ist ein Funktor in seinen beiden Argumenten. In Haskell können wir jedoch (direkt) nur Funktoren in den letzten Argumenten eines Typs definieren. In Fällen wie diesem können wir diese Einschränkung umgehen, indem wir newtype s:

newtype FlipEither b a = FlipEither { unFlipEither :: Either a b }

Wir haben also einen Konstruktor FlipEither :: Either a b -> FlipEither b a die eine Hülle aus Either in unser newtype mit vertauschten Typargumenten. Und wir haben den Dectructor unFlipEither :: FlipEither b a -> Either a b der es wieder zurückverwandelt. Jetzt können wir eine Funktorinstanz definieren in FlipEither ist das letzte Argument, das eigentlich Either das erste Argument:

instance Functor (FlipEither b) where
    fmap f (FlipEither (Left x))  = FlipEither (Left (f x))
    fmap f (FlipEither (Right x)) = FlipEither (Right x)

Beachten Sie: Wenn wir vergessen FlipEither eine Zeit lang nur die Definition von Functor pour Either nur mit Left / Right ausgetauscht. Und jetzt, wann immer wir eine Functor Instanz in Either können wir den Wert in das erste Typ-Argument einpacken FlipEither und wickeln Sie es danach aus. Zum Beispiel:

fmapE2 :: (a -> b) -> Either a c -> Either b c
fmapE2 f = unFlipEither . fmap f . FlipEither

Aktualisierung: Werfen Sie einen Blick auf Daten.Bifunctor , davon Either y (,) sind Instanzen von. Jede Bifunktor hat zwei Argumente und ist in jedem von ihnen ein Funktor. Dies spiegelt sich wider in Bifunctor Die Methoden der first y second .

Die Definition von Bifunctor de Either ist sehr symetrisch:

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

    first  f = bimap f id

    second f = bimap id f

1voto

jbolden1517 Punkte 329

Nun versuche ich zu verstehen, warum die Implementierung im Fall eines Right value constructor, aber nicht im Falle eines Left?

Schließen Sie hier an und es könnte Sinn machen.

Angenommen a = String (eine Fehlermeldung) Sie wenden entweder a auf einen Float an.

Sie haben also ein f: Float -> Integer sagen wir zum Beispiel roundoff.

(Either String) (Float) = Either String Float.

now (fmap f):: Either String Float -> Either String Int Was willst du also mit f machen? f hat keine Ahnung, was man mit Strings machen kann, also kann man da nichts machen. Das ist natürlich können Sie nur auf die rechten Werte einwirken und die linken Werte unverändert lassen.

Mit anderen Worten: Entweder ist a ein Funktor, weil es eine solche offensichtliche fmap gibt, die durch gegeben ist:

  • für Rechte Werte gelten f
  • für Linke Werte nichts tun

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X