Die Haskell-Semantik verwendet einen "unteren Wert", um die Bedeutung des Haskell-Codes zu analysieren. Es ist nicht etwas, das Sie wirklich direkt beim Programmieren von Haskell verwenden, und das Zurückkehren None
ist überhaupt nicht dasselbe.
Der unterste Wert ist der Wert, den die Haskell-Semantik jeder Berechnung zuweist, die nicht normal bewertet werden kann. Eine solche Möglichkeit, die eine Haskell-Berechnung bietet, besteht darin, eine Ausnahme auszulösen! Wenn Sie also versuchen, diesen Stil in Python zu verwenden, sollten Sie eigentlich wie gewohnt Ausnahmen auslösen.
Die Haskell-Semantik verwendet den unteren Wert, da Haskell faul ist. Sie können "Werte" manipulieren, die von Berechnungen zurückgegeben werden, die noch nicht ausgeführt wurden. Sie können sie an Funktionen übergeben, in Datenstrukturen speichern usw. Eine solche nicht ausgewertete Berechnung kann eine Ausnahme oder eine Endlosschleife auslösen. Wenn wir den Wert jedoch nie tatsächlich untersuchen müssen, wird die Berechnung dies niemals tunFühren Sie den Befehl aus, und stellen Sie fest, dass der Fehler aufgetreten ist. Unser Gesamtprogramm schafft es möglicherweise, eine genau definierte Aufgabe zu erledigen und zu beenden. Ohne zu erklären, was Haskell-Code bedeutet, indem das genaue Betriebsverhalten des Programms zur Laufzeit angegeben wird, deklarieren wir stattdessen, dass solche fehlerhaften Berechnungen den unteren Wert erzeugen, und erklären, wie sich dieser Wert verhält. Grundsätzlich gilt, dass jeder Ausdruck, der von allen Eigenschaften des unteren Werts (der nicht vorhanden ist) abhängen muss, auch den unteren Wert ergibt.
Um "rein" zu bleiben, müssen alle Möglichkeiten zur Generierung des Grundwerts als gleichwertig behandelt werden. Das schließt den "unteren Wert" ein, der eine Endlosschleife darstellt. Da es keine Möglichkeit gibt, zu wissen, dass einige Endlosschleifen tatsächlich unendlich sind (sie werden möglicherweise beendet, wenn Sie sie nur etwas länger ausführen), können Sie keine Eigenschaft mit einem unteren Wert untersuchen. Sie können nicht testen, ob etwas unten ist, können es nicht mit etwas anderem vergleichen, können es nicht in eine Zeichenfolge konvertieren, nichts. Alles, was Sie mit einer tun können, ist, sie unberührt und ungeprüft zu platzieren (Funktionsparameter, Teil einer Datenstruktur usw.).
Python hat diese Art von Grund bereits; Es ist der "Wert", den Sie von einem Ausdruck erhalten, der eine Ausnahme auslöst oder nicht beendet. Da Python eher streng als faul ist, können solche "Unterteile" nirgendwo gespeichert und möglicherweise ungeprüft gelassen werden. Es ist also nicht wirklich notwendig, das Konzept des unteren Werts zu verwenden, um zu erklären, wie Berechnungen, die keinen Wert zurückgeben, weiterhin so behandelt werden können, als ob sie einen Wert hätten. Aber es gibt auch keinen Grund, warum Sie nicht so über Ausnahmen nachdenken könnten, wenn Sie wollten.
Das Auslösen von Ausnahmen wird tatsächlich als "rein" betrachtet. Es sind auffällige Ausnahmen, die die Reinheit brechen - gerade weil Sie damit etwas über bestimmte Grundwerte prüfen können, anstatt sie alle austauschbar zu behandeln. In Haskell können Sie nur Ausnahmen abfangen IO
, die eine unreine Schnittstelle zulassen (dies geschieht normalerweise in einer ziemlich äußeren Schicht). Python erzwingt keine Reinheit, aber Sie können selbst entscheiden, welche Funktionen zu Ihrer "äußeren unreinen Schicht" gehören und nicht zu reinen Funktionen, und nur zulassen, dass Sie dort Ausnahmen abfangen.
Die Rückkehr None
ist völlig anders. None
ist ein nicht unterster Wert; Sie können testen, ob etwas damit übereinstimmt, und der Aufrufer der zurückgegebenen Funktion None
wird weiterhin ausgeführt, möglicherweise unter Verwendung von None
.
Wenn Sie also daran denken, eine Ausnahme auszulösen, und "nach unten zurückkehren" möchten, um Haskells Ansatz zu emulieren, tun Sie einfach gar nichts. Lass die Ausnahme sich verbreiten. Genau das meinen Haskell-Programmierer, wenn sie über eine Funktion sprechen, die einen unteren Wert zurückgibt.
Aber das ist nicht das, was funktionierende Programmierer meinen, wenn sie Ausnahmen vermeiden wollen. Funktionale Programmierer bevorzugen "Gesamtfunktionen". Diese geben für jede mögliche Eingabe immer einen gültigen Non-Bottom-Wert ihres Rückgabetyps zurück . Jede Funktion, die eine Ausnahme auslösen kann, ist also keine Gesamtfunktion.
Der Grund, warum wir Total Functions mögen, ist, dass sie viel einfacher als "Black Boxes" zu behandeln sind, wenn wir sie kombinieren und manipulieren. Wenn ich eine Total-Funktion habe, die etwas vom Typ A zurückgibt, und eine Total-Funktion, die etwas vom Typ A akzeptiert, dann kann ich die Sekunde am Ausgang der ersten aufrufen, ohne etwas über die Implementierung von beiden zu wissen ; Ich weiß, dass ich ein gültiges Ergebnis erhalten werde, unabhängig davon, wie der Code einer der beiden Funktionen in Zukunft aktualisiert wird (solange ihre Gesamtheit erhalten bleibt und solange sie dieselbe Typensignatur behalten). Diese Trennung von Bedenken kann ein äußerst wirksames Hilfsmittel für das Refactoring sein.
Es ist auch etwas notwendig für zuverlässige Funktionen höherer Ordnung (Funktionen, die andere Funktionen manipulieren). Wenn ich Code schreiben möchten , die eine völlig willkürliche Funktion (mit einer bekannten Schnittstelle) als Parameter erhält ich habe es als Blackbox zu behandeln , weil ich keine Möglichkeit zu wissen , haben die Eingänge möglicherweise einen Fehler auslösen. Wenn mir eine Gesamtfunktion gegeben wird, verursacht keine Eingabe einen Fehler. Ebenso weiß der Aufrufer meiner übergeordneten Funktion nicht genau, welche Argumente ich verwende, um die von ihm übergebene Funktion aufzurufen (es sei denn, er möchte von meinen Implementierungsdetails abhängen). Wenn er also eine Gesamtfunktion übergibt, muss er sich keine Sorgen machen was ich damit mache.
Ein funktionaler Programmierer, der Ihnen rät, Ausnahmen zu vermeiden, würde es vorziehen, stattdessen einen Wert zurückzugeben, der entweder den Fehler oder einen gültigen Wert codiert, und der voraussetzt, dass Sie bereit sind, beide Möglichkeiten zu nutzen. Dinge wie Either
Typen oder Maybe
/ Option
-Typen sind einige der einfachsten Ansätze, um dies in stärker typisierten Sprachen zu tun (normalerweise verwendet mit spezieller Syntax oder Funktionen höherer Ordnung, um Dinge, die ein brauchen, A
mit Dingen zusammenzukleben, die ein erzeugen Maybe<A>
).
Eine Funktion, die entweder None
(wenn ein Fehler aufgetreten ist) oder einen Wert (wenn kein Fehler aufgetreten ist) zurückgibt, folgt keiner der oben genannten Strategien.
In Python mit Duck-Typisierung wird der Either / Maybe-Stil nicht sehr häufig verwendet. Stattdessen werden Ausnahmen ausgelöst, um zu überprüfen, ob der Code funktioniert, und nicht um sicherzustellen, dass Funktionen vollständig sind und basierend auf ihren Typen automatisch kombiniert werden können. Python hat keine Möglichkeit, zu erzwingen, dass Code Dinge wie Vielleicht Typen richtig verwendet. Selbst wenn Sie es aus Gründen der Disziplin einsetzen, benötigen Sie Tests, um Ihren Code tatsächlich auszuüben und dies zu überprüfen. Daher ist der Ausnahmen- / Bottom-Ansatz wahrscheinlich eher für die reine Funktionsprogrammierung in Python geeignet.