Faire Warnung, ich bin neu in der funktionalen Programmierung, daher kann ich viele schlechte Annahmen vertreten.
Ich habe etwas über algebraische Typen gelernt. Viele funktionale Sprachen scheinen sie zu haben, und sie sind in Verbindung mit dem Mustervergleich ziemlich nützlich. Welches Problem lösen sie jedoch tatsächlich? Ich kann einen scheinbar (irgendwie) algebraischen Typ in C # wie folgt implementieren:
public abstract class Option { }
public class None : Option { }
public class Some<T> : Option
{
public T Value { get; set; }
}
var result = GetSomeValue();
if(result is None)
{
}
else
{
}
Aber ich denke, die meisten würden zustimmen, dass dies eine Bastardisierung der objektorientierten Programmierung ist, und Sie sollten dies niemals tun. Fügt die funktionale Programmierung nur eine klarere Syntax hinzu, die diesen Programmierstil weniger grob erscheinen lässt? Was fehlt mir noch?
functional-programming
algebraic-data-type
ConditionRacer
quelle
quelle
class ThirdOption : Option{}
und dir ein gebe,new ThirdOption()
wo du es erwartet hastSome
oderNone
?data Maybe a = Just a | Nothing
(äquivalent zudata Option a = Some a | None
in Ihrem Beispiel): Sie können keinen dritten Fall post-hoc hinzufügen. Zwar können Sie Summentypen in C # so emulieren, wie Sie es gezeigt haben, aber es ist nicht das Schönste.Antworten:
Klassen mit Schnittstellen und Vererbung stellen eine offene Welt dar: Jeder kann eine neue Art von Daten hinzufügen. Für eine bestimmte Schnittstelle kann es Klassen geben, die sie weltweit in verschiedenen Dateien, in verschiedenen Projekten und in verschiedenen Unternehmen implementieren. Sie erleichtern das Hinzufügen von Fällen zu den Datenstrukturen, aber da die Implementierungen der Schnittstelle dezentral sind, ist es schwierig, der Schnittstelle eine neue Methode hinzuzufügen. Sobald eine Schnittstelle öffentlich ist, ist sie grundsätzlich eingefroren. Niemand kennt alle möglichen Implementierungen.
Algebraische Datentypen sind das Doppelte, sie sind geschlossen . Alle Fälle der Daten werden an einer Stelle aufgelistet, und Vorgänge können die Varianten nicht nur vollständig auflisten , sie werden auch dazu aufgefordert. Folglich ist das Schreiben einer neuen Funktion, die mit einem algebraischen Datentyp arbeitet, trivial: Schreiben Sie einfach die verdammte Funktion. Im Gegenzug ist das Hinzufügen neuer Fälle kompliziert, da Sie im Grunde die gesamte Codebasis durchgehen und jede erweitern müssen
match
. Ähnlich wie bei Schnittstellen ist das Hinzufügen einer neuen Variante in der Rust-Standardbibliothek eine grundlegende Änderung (für öffentliche Typen).Dies sind zwei Seiten des Ausdrucksproblems . Algebraische Datentypen sind für sie eine unvollständige Lösung, OOP jedoch auch. Beide haben Vorteile, je nachdem, wie viele Datenfälle vorhanden sind, wie oft sich diese Fälle ändern und wie oft die Vorgänge erweitert oder geändert werden. (Aus diesem Grund bieten viele moderne Sprachen beides oder etwas Ähnliches an oder setzen direkt auf leistungsfähigere und kompliziertere Mechanismen, die beide Ansätze zu subsumieren versuchen.)
quelle
Das ist vielleicht eine Vereinfachung, aber ja.
Lassen Sie uns klarstellen, welche algebraischen Datentypen vorliegen (Zusammenfassung dieses feinen Links aus Learn you as Haskell):
Ihr Beispiel funktioniert wirklich nur mit dem ersten.
Was Sie vielleicht vermissen, ist, dass Sie durch die Bereitstellung dieser beiden Grundoperationen mit funktionalen Sprachen alles andere aufbauen können. C # enthält darüber hinaus Strukturen, Klassen, Aufzählungen, Generika und Stapel von Regeln, mit denen das Verhalten dieser Dinge gesteuert werden kann.
In Kombination mit einer gewissen Syntax können die funktionalen Sprachen Operationen auf diese beiden Pfade aufteilen und so einen sauberen, einfachen und eleganten Ansatz für Typen bieten.
Sie lösen das gleiche Problem wie jedes andere Typsystem: "Welche Werte sind hier zulässig?" - Sie gehen einfach anders vor.
quelle
Es kann Sie überraschen, dass der Mustervergleich nicht als die idiomatischste Methode für die Arbeit mit Optionen angesehen wird. Weitere Informationen hierzu finden Sie in der Dokumentation zu Scala Options . Ich bin mir nicht sicher, warum so viele FP-Tutorials diese Verwendung fördern.
Meistens fehlt Ihnen eine ganze Reihe von Funktionen, um die Arbeit mit Optionen zu vereinfachen. Betrachten Sie das Hauptbeispiel aus den Scala-Dokumenten:
Beachten Sie, wie
map
undfilter
lassen Sie Operationen an Optionen verketten, ohne an jedem Punkt überprüfen zu müssen, ob Sie dies getan habenNone
oder nicht. Am EndegetOrElse
geben Sie einen Standardwert an. Zu keinem Zeitpunkt machen Sie etwas "Grobes" wie das Prüfen von Typen. Jede unvermeidbare Typprüfung erfolgt bibliotheksintern. Haskell hat seine eigenen analogen Funktionen, ganz zu schweigen von den zahlreichen Funktionen, die auf jeder Monade oder jedem Functor funktionieren.Andere algebraische Datentypen haben ihre eigene idiomatische Arbeitsweise, und in den meisten Fällen ist die Mustererkennung am Totempfahl gering. Wenn Sie eigene Typen erstellen, müssen Sie ähnliche Funktionen bereitstellen, um mit ihnen arbeiten zu können.
quelle