2010-06-20 10 views
9

Wie extrahieren Sie einen Wert aus einer Variablen eines unbekannten Konstruktors?Mustererkennung in einem Let-Ausdruck

Zum Beispiel würde Ich mag den Wert in einem Entweder negieren, wenn sie als Recht errichtet wurde:

let Right x = getValue 
in Right (negate x)

Dieser Code bindet erfolgreich rechten Wert (ein Int in diesem Fall) zu x.

Das funktioniert, aber was, wenn getValue eine Left stattdessen zurückgibt? Gibt es eine Möglichkeit, den Typ einer Variablen in einem let-Ausdruck zu bestimmen? Oder gibt es einen besseren Weg, dieses Problem anzugehen?

Antwort

17

Im Allgemeinen, was Sie tun können, ist dies:

case getValue of 
    Right x -> Right $ negate x 
    e  -> e 

Was sollte dies klar macht sein: es ist wie Muster in einem Funktionsargument übereinstimmt, aber gegen einen Wert. Um zu tun, was Sie brauchen, haben Sie einen Standardfall, der alles fängt, was nicht übereinstimmt, und dann das zurückgeben.

negate `fmap` getValue 

Oder mit import Control.Applicative Sie <$> als Synonym für fmap (negate <$> getValue) verwenden können:

In Ihrem speziellen Fall aber können Sie etwas etwas schöner machen. Die fmap-Funktion hat den Typ fmap :: Functor f => (a -> b) -> f a -> f b. Für jeden Funktor wandelt fmap eine Funktion auf gewöhnlichen Werten in eine Funktion innerhalb des Funktors um. Zum Beispiel sind Listen ein Funktor und für Listen fmap = map. Hier stellt Either e einen Funktor dar, der entweder eine Ausnahme Left e oder ein Wert Right a ist; das Anwenden einer Funktion auf Left tut nichts, aber das Anwenden einer Funktion auf Right wendet es innerhalb der Right an. Mit anderen Worten,

instance Functor (Either e) where 
    fmap _ (Left l) = Left l 
    fmap f (Right r) = Right $ f r 

So ist die case Version ist die direkte Antwort auf Ihre Frage, aber Ihr besonderes Beispiel wird durch fmap mehr schön angenähert.

1: In erster Näherung sind Funktoren "Container". Wenn Sie mit den verschiedenen Klassen nicht vertraut sind, empfehle ich the Typeclassopedia für eine umfassende Referenz; Es gibt viele weitere Tutorials, und der beste Weg, ein Gefühl für sie zu bekommen, ist, einfach mit ihnen zu spielen. Jedoch ist die fmap für spezifische Typen oft leicht verwendbar (insbesondere meiner Meinung nach, wenn geschrieben <$>).

+4

Und vergessen Sie nicht schön 'entweder Left (Right negieren.) GetValue' von' Data.Either';) – ony

+0

Vielen Dank. Der Fall Ausdruck ist, was ich gesucht habe. Ich verstehe den Rest deiner Antwort nicht; Ich werde darauf zurückkommen, sobald ich mehr gelernt habe. – titaniumdecoy

+0

@ony: Ich denke, du solltest deinen Kommentar als separate Antwort schreiben, es ist wirklich die Zwischenlösung zwischen dem 'Fall' und dem' fmap'! – yatima2975

2

Antwort auf den Titel dieser Frage:
ich keinen großen Unterschied zwischen „... where“ sehen und „let ... in ...“. Beide können Sie mehrere Fälle von Funktionsargument Bindungen erklären zu tun:

f val = let negR (Right x) = Right (negate x) 
      negR y = y 
     in negR val 

oder let { negR (Right x) = Right (negate x); negR y = y; } in negR val