In Haskell kann ich den Typ verwenden a -> Maybe b
, um eine Funktion zu modellieren, die entweder einen Wert vom Typ b
zurückgibt oder nichts zurückgibt (dies schlägt fehl).
Wenn ich Typen a1, ..., a(n+1)
und Funktionen f1, ..., fn
, mit fi :: ai -> Maybe a(i+1)
für alle i
, 1 <= i <= n
kann ich die Kette , die Funktionen durch die Verwendung des >>=
Betreibers der Maybe
Monade und schreiben:
f1 x >>= f2 >>= f3 >>=... >>= fn
Der >>=
Operator stellt sicher, dass jede Funktion angewendet wird, solange der Vorgänger einen aussagekräftigen Wert zurückgegeben hat. Sobald eine Funktion in der Kette ausfällt, fällt die gesamte Kette aus (kehrt zurück Nothing
) und weitere Funktionen in der Kette werden nicht ausgewertet.
Ich habe ein etwas ähnliches Muster, bei dem ich mehrere Funktionen am selben Eingang ausprobieren und zurückkehren möchte, sobald eine Funktion erfolgreich ist . Wenn alle Funktionen fehlschlagen (return Nothing
), sollte die gesamte Berechnung fehlschlagen. Genauer gesagt habe ich Funktionen f1, ..., fn :: a -> Maybe b
und definiere die Funktion
tryFunctions :: [a -> Maybe b] -> a -> Maybe b
tryFunctions [] _ = Nothing
tryFunctions (f : fs) x = case f x of
Nothing -> tryFunctions fs x
r@(Just _) -> r
In gewissem Sinne ist dies für die Maybe
Monade insofern dual , als eine Berechnung beim ersten Erfolg statt beim ersten Misserfolg stoppt.
Natürlich kann ich die oben beschriebene Funktion verwenden, aber ich habe mich gefragt, ob es eine bessere, gut etablierte und idiomatische Möglichkeit gibt, dieses Muster in Haskell auszudrücken.
return f1 ?? f2 ?? f3 ?? DefaultValue;
Alternative
der Symbol-Infix-Operator<|>
und wird als Monoid definiertAntworten:
Bei einer geschlossenen Menge (feste Anzahl von Elementen)
S
mit Elementen{a..z}
und einem binären Operator*
:Es gibt ein einziges Identitätselement,
i
so dass:forall x in S: i * x = x = x * i
Der Operator ist assoziativ, so dass:
forall a, b, c in S: a * (b * c) = (a * b) * c
Du hast ein Monoid.
Wenn Sie nun ein beliebiges Monoid haben, können Sie eine Binärfunktion
f
wie folgt definieren :Dies bedeutet, dass für das Beispiel des
Maybe
Monoids (Nothing
ist das oben als bezeichnet bezeichnete Identitätselementi
):Überraschenderweise kann ich diese genaue Funktion in den Standardbibliotheken nicht finden, was wahrscheinlich auf meine eigene Unerfahrenheit zurückzuführen ist. Wenn jemand anderes dies freiwillig tun kann, würde ich es aufrichtig schätzen.
Hier ist die Implementierung, die ich aus dem obigen Beispiel abgeleitet habe:
Beispiel:
Dann, wenn Sie eine Liste von Funktionen als Eingabe verwenden möchten ...
Beispiel:
quelle
<|>
die Identität eines Monoids auf besondere Weise behandelt wird. Könnte man nicht ein beliebiges Element einer beliebigen Menge auswählen, um diese besondere Rolle zu spielen? Warum muss das Set ein Monoid sein und das besondere Element<|>
seiner Identität?<|>
nicht auf Monoid und ich habe das alles durcheinander gebracht? Es basiert aufAlternative
Typklasse. Um sicher zu sein - ich schaue auf meine eigene Antwort und stelle fest, dass sie nicht ganz richtig ist, da sie[1,2] <|> [3]
das Unerwartete ergibt,[1,2,3]
sodass alles an der Verwendung der Monoidtypklasse zur Identifizierung einer Identität richtig ist - und der andere Schlüssel ist die Assoziativität, die erforderlich ist, um das erwartete Verhalten zu erhalten , gibt vielleichtAlternative
nicht das Verhalten, das ich aus der Hand gedacht habe ...f
zu sehen, warum Assoziativität eine Notwendigkeit ist.quelle
Maybe
... Meine Lösung ist eingeschränkt durchEq
, irgendwie habe ich das Gefühl, dass wir beide etwas vermissen, dasMonoid
allgemein verfügbar ist ...either
.Monoid
, dass alles mit einem Identitätselement einen Satz von 2 Funktionen haben könnte (ich denke, dies ist eine Art Binärfeld), wobei eine der Funktionen die Identität über alle anderen wählt und die andere Funktion immer wählt das Nichtidentitätselement. Ich weiß nur nicht, wie ich das machen soll, ohneEq
zu wissen, welcher Wert istidentity
oder nicht. Offensichtlich erhalten Sie in additiven oder multiplikativen Monoiden standardmäßig die Kontaktplanfunktion (wählen Sie immer Nichtidentitätselemente mit der Monoid-Binärfunktion)foldMap
kann anstelle vonmconcat . map
Das klingt sehr danach , Fehler durch eine Liste von Erfolgen zu ersetzen
Sie sprechen
Maybe a
eher über als[a]
, aber in der Tat sind sie sich sehr ähnlich: Wir können uns vorstellen, dass esMaybe a
so ist[a]
, außer dass es höchstens ein Element enthalten kann (dhNothing ~= []
undJust x ~= [x]
).Bei Listen
tryFunctions
wäre dies sehr einfach: Wenden Sie alle Funktionen auf das angegebene Argument an und verketten Sie dann alle Ergebnisse miteinander.concatMap
wird das schön machen:Auf diese Weise können wir sehen, dass der
<|>
Operator fürMaybe
wie eine Verkettung für 'Listen mit höchstens einem Element' wirkt.quelle
Teilen Sie es im Geiste von Conal in kleinere, einfachere Operationen auf.
In diesem Fall
asum
ausData.Foldable
macht den Hauptteil.Alternativ können Sie die
Monoid
Instanz von a la Jimmy Hoffa für verwenden,(->)
aber dann benötigen Sie eineMonoid
Instanz fürMaybe
und die Standardinstanz macht nicht das, was Sie wollen. Du willstFirst
vonData.Monoid
.(Oder
mconcat
für eine ältere, spezialisiertere Version vonfold
.)quelle