Ich kann den Punkt der Option[T]
Klasse in Scala nicht verstehen . Ich meine, ich kann keine Vorteile von None
über sehen null
.
Betrachten Sie zum Beispiel den Code:
object Main{
class Person(name: String, var age: int){
def display = println(name+" "+age)
}
def getPerson1: Person = {
// returns a Person instance or null
}
def getPerson2: Option[Person] = {
// returns either Some[Person] or None
}
def main(argv: Array[String]): Unit = {
val p = getPerson1
if (p!=null) p.display
getPerson2 match{
case Some(person) => person.display
case None => /* Do nothing */
}
}
}
Angenommen, die Methode getPerson1
kehrt zurück null
, und der Aufruf in der display
ersten Zeile von main
schlägt mit fehl NPE
. In ähnlicher Weise getPerson2
schlägt None
der display
Aufruf bei einem ähnlichen Fehler erneut fehl, wenn er zurückkehrt .
Wenn ja, warum erschwert Scala die Dinge, indem ein neuer Value Wrapper ( Option[T]
) eingeführt wird, anstatt einem einfachen Ansatz zu folgen, der in Java verwendet wird?
AKTUALISIEREN:
Ich habe meinen Code gemäß dem Vorschlag von @Mitch bearbeitet . Ich sehe immer noch keinen besonderen Vorteil von Option[T]
. Ich muss auf das Außergewöhnliche null
oder None
in beiden Fällen testen . :(
Wenn ich die Antwort von @ Michael richtig verstanden habe , besteht der einzige Vorteil Option[T]
darin, dass der Programmierer explizit darüber informiert wird, dass diese Methode None zurückgeben kann . Ist dies der einzige Grund für diese Designentscheidung?
get
, und Sie werden bekommen es. :-)Antworten:
Sie werden es
Option
besser machen, wenn Sie sich zwingen, es niemals zu benutzenget
. Das liegt daran, dassget
es das Äquivalent von "ok, schick mich zurück ins Nullland" ist.Nehmen Sie also Ihr Beispiel. Wie würden Sie
display
ohne Verwendung anrufenget
? Hier sind ein paar alternativen:Mit keiner dieser Alternativen können Sie
display
etwas aufrufen , das nicht existiert.Was , warum
get
existiert, ist Scala nicht sagen Ihnen , wie Sie den Code geschrieben werden soll. Es mag Sie sanft anstoßen, aber wenn Sie auf kein Sicherheitsnetz zurückgreifen möchten, haben Sie die Wahl.Du hast es hier geschafft:
Bis auf das "einzige". Aber lassen Sie mich neu formulieren , dass in einer anderen Art und Weise: Der Hauptvorteil von
Option[T]
überT
ist Typsicherheit. Es stellt sicher, dass Sie keineT
Methode an ein Objekt senden , das möglicherweise nicht vorhanden ist, da der Compiler dies nicht zulässt.Sie sagten, Sie müssen in beiden Fällen auf Nullfähigkeit testen, aber wenn Sie vergessen oder nicht wissen, dass Sie auf Null prüfen müssen, wird Ihnen der Compiler dies mitteilen? Oder werden Ihre Benutzer?
Natürlich erlaubt Scala aufgrund seiner Interoperabilität mit Java genau wie Java Nullen. Wenn Sie also Java-Bibliotheken verwenden, wenn Sie schlecht geschriebene Scala-Bibliotheken verwenden oder wenn Sie schlecht geschriebene persönliche Scala-Bibliotheken verwenden, müssen Sie sich immer noch mit Nullzeigern befassen.
Weitere zwei wichtige Vorteile, die
Option
mir einfallen, sind:Dokumentation: Eine Signatur vom Methodentyp gibt an, ob ein Objekt immer zurückgegeben wird oder nicht.
Monadische Kompositionsfähigkeit.
Letzteres dauert viel länger, um es vollständig zu verstehen, und es eignet sich nicht gut für einfache Beispiele, da es nur seine Stärke in komplexem Code zeigt. Also, ich werde unten ein Beispiel geben, aber ich bin mir bewusst, dass es kaum etwas bedeuten wird, außer für die Leute, die es bereits bekommen.
quelle
get
" -> Also, mit anderen Worten: "Du tust es nichtget
!" :)Vergleichen Sie:
mit:
Die monadische Eigenschaft binden , die in Scala als erscheint Kartenfunktion ermöglicht es uns, Chain - Operationen auf Objekten ohne sich Gedanken darüber , ob es sich um ‚Null‘ oder nicht.
Nehmen Sie dieses einfache Beispiel etwas weiter. Angenommen, wir wollten alle Lieblingsfarben einer Personenliste finden.
Oder vielleicht möchten wir den Namen der Schwester der Mutter eines Vaters finden:
Ich hoffe, dies gibt Aufschluss darüber, wie Optionen das Leben ein wenig erleichtern können.
quelle
map
wird zurückkehrenNone
und der Anruf würde mit einem Fehler fehlschlagen. Wie ist es besser als dernull
Ansatz?Der Unterschied ist subtil. Denken Sie daran, um wirklich eine Funktion zu sein , die einen Wert zurückgeben muss - null wird in diesem Sinne nicht wirklich als "normaler Rückgabewert" betrachtet, sondern eher als unterer Typ / nichts.
In praktischer Hinsicht würden Sie jedoch Folgendes tun, wenn Sie eine Funktion aufrufen, die optional etwas zurückgibt:
Zugegeben, Sie können mit null etwas Ähnliches tun - aber dies macht die Semantik des Aufrufs
getPerson2
aufgrund der Tatsache, dass es zurückkehrt , offensichtlichOption[Person]
(eine nette praktische Sache, außer sich darauf zu verlassen, dass jemand das Dokument liest und eine NPE erhält, weil er das nicht liest doc).Ich werde versuchen, einen funktionierenden Programmierer zu finden, der eine strengere Antwort geben kann als ich.
quelle
Für mich sind Optionen wirklich interessant, wenn sie zum Verständnis der Syntax verwendet werden. Unter synesso vorhergehenden Beispiel:
Wenn eine der Zuweisungen vorhanden ist
None
,fathersMothersSister
wird die ZuweisungNone
jedoch nichtNullPointerException
erhöht. Sie können dann sicherfathersMothersSister
an eine Funktion übergeben, die Optionsparameter übernimmt, ohne sich Sorgen machen zu müssen. Sie suchen also nicht nach Null und kümmern sich nicht um Ausnahmen. Vergleichen Sie dies mit der Java-Version im Synesso- Beispiel.quelle
<-
Syntax in Scala auf die "Listenverständnis-Syntax" beschränkt, da sie tatsächlich mit der allgemeinerendo
Syntax von Haskell oder derdomonad
Form aus Clojures Monadenbibliothek identisch ist . Das Binden an Listen verkauft es kurz.Mit Option haben Sie ziemlich mächtige Kompositionsmöglichkeiten:
quelle
Vielleicht hat jemand anderes darauf hingewiesen, aber ich habe es nicht gesehen:
Ein Vorteil des Mustervergleichs mit Option [T] gegenüber der Nullprüfung besteht darin, dass Option eine versiegelte Klasse ist. Daher gibt der Scala-Compiler eine Warnung aus, wenn Sie den Fall Some oder None nicht codieren. Der Compiler verfügt über ein Compiler-Flag, das Warnungen in Fehler umwandelt. Auf diese Weise kann verhindert werden, dass der Fall "nicht vorhanden" nicht zur Laufzeit, sondern zur Laufzeit behandelt wird. Dies ist ein enormer Vorteil gegenüber der Verwendung des Nullwerts.
quelle
Es ist nicht dazu da, eine Nullprüfung zu vermeiden, sondern eine Nullprüfung zu erzwingen. Der Punkt wird klar, wenn Ihre Klasse 10 Felder hat, von denen zwei null sein können. Und Ihr System hat 50 ähnliche Klassen. In der Java-Welt versuchen Sie, NPEs in diesen Feldern zu verhindern, indem Sie eine Kombination aus geistiger Leistungsfähigkeit, Namenskonvention oder vielleicht sogar Anmerkungen verwenden. Und jeder Java-Entwickler scheitert daran in erheblichem Maße. Die Option-Klasse macht nicht nur Entwicklern, die versuchen, den Code zu verstehen, "nullfähige" Werte visuell klar, sondern ermöglicht dem Compiler auch, diesen zuvor unausgesprochenen Vertrag durchzusetzen.
quelle
[kopiert von diesem Kommentar von Daniel Spiewak ]
quelle
Option
fürNone
wieder. Wären die Anweisungen als verschachtelte Bedingungen geschrieben worden, würde jeder potenzielle "Fehler" nur einmal getestet und bearbeitet. In Ihrem Beispiel über das ErgebnisfetchRowById
effektiv kontrolliert wird drei Mal: einmal führenkey
‚s Initialisierung wieder fürvalue
‘ s, und schließlich fürresult
‚s. Es ist eine elegante Art, es zu schreiben, aber es ist nicht ohne Laufzeitkosten.Ein Punkt, den hier sonst niemand angesprochen zu haben scheint, ist, dass Sie zwar eine Nullreferenz haben können, aber durch Option eine Unterscheidung eingeführt wird.
Das heißt, Sie können haben
Option[Option[A]]
, die von bewohnt würdenNone
,Some(None)
undSome(Some(a))
woa
ist einer der üblichen Bewohner vonA
. Dies bedeutet, dass Sie, wenn Sie eine Art Container haben und in der Lage sein möchten, Nullzeiger darin zu speichern und sie herauszuholen, einen zusätzlichen booleschen Wert zurückgeben müssen, um zu wissen, ob Sie tatsächlich einen Wert herausbekommen haben. Warzen wie diese sind in den Java-Container-APIs im Überfluss vorhanden, und einige sperrfreie Varianten können sie nicht einmal bereitstellen.null
ist eine einmalige Konstruktion, komponiert nicht mit sich selbst, ist nur für Referenztypen verfügbar und zwingt Sie dazu, nicht vollständig zu argumentieren.Zum Beispiel, wenn Sie überprüfen
Sie müssen in Ihrem Kopf in der gesamten
else
Branche herumtragen, dassx != null
und dass dies bereits überprüft wurde. Wenn Sie jedoch so etwas wie eine Option verwendenSie wissen, dass Sie nicht
None
konstruktionsbedingt sind - und Sie würden wissennull
, dass dies auch nicht der Fall war , wenn Hoares Milliarden-Dollar-Fehler nicht gewesen wäre .quelle
Option [T] ist eine Monade, die sehr nützlich ist, wenn Sie Funktionen höherer Ordnung zum Bearbeiten von Werten verwenden.
Ich schlage vor, dass Sie die unten aufgeführten Artikel lesen. Es sind wirklich gute Artikel, die Ihnen zeigen, warum Option [T] nützlich ist und wie es auf funktionale Weise verwendet werden kann.
quelle
Um Randalls Teaser einer Antwort zu ergänzen und zu verstehen, warum das potenzielle Fehlen eines Wertes durch dargestellt wird,
Option
muss man verstehen, wasOption
mit vielen anderen Typen in Scala gemeinsam ist - insbesondere mit Typen, die Monaden modellieren. Wenn einer das Fehlen eines Wertes mit null darstellt, kann diese Unterscheidung zwischen Abwesenheit und Anwesenheit nicht an den Verträgen teilnehmen, die von den anderen monadischen Typen geteilt werden.Wenn Sie nicht wissen, was Monaden sind, oder wenn Sie nicht bemerken, wie sie in Scalas Bibliothek dargestellt werden, werden Sie nicht sehen, was
Option
mitspielt, und Sie können nicht sehen, was Sie verpassen. Es gibt viele Vorteile bei der VerwendungOption
anstelle von Null, die selbst ohne ein Monadenkonzept bemerkenswert wären (ich diskutiere einige davon im Scala-Benutzer- Mailinglisten-Thread "Kosten der Option / Einige gegen Null" hier ), aber ich spreche darüber Die Isolation ist so, als würde man über den Iteratortyp einer bestimmten Implementierung einer verknüpften Liste sprechen und sich fragen, warum dies notwendig ist, während die allgemeinere Schnittstelle zwischen Container, Iterator und Algorithmus fehlt. Auch hier gibt es eine breitere Oberfläche.Option
quelle
Ich denke, der Schlüssel liegt in Synessos Antwort: Option ist nicht in erster Linie als umständlicher Alias für Null nützlich, sondern als vollwertiges Objekt, das Ihnen dann bei Ihrer Logik helfen kann.
Das Problem mit null ist, dass es das Fehlen eines Objekts ist. Es gibt keine Methoden, die Ihnen dabei helfen könnten (obwohl Sie als Sprachdesigner Ihrer Sprache immer längere Listen von Funktionen hinzufügen können, die ein Objekt emulieren, wenn Sie wirklich Lust dazu haben).
Wie Sie gezeigt haben, kann Option eine Null emulieren. Sie müssen dann den außergewöhnlichen Wert "Keine" anstelle des außergewöhnlichen Werts "null" testen. Wenn Sie vergessen, werden in beiden Fällen schlimme Dinge passieren. Option macht es weniger wahrscheinlich, dass es versehentlich passiert, da Sie "get" eingeben müssen (was Sie daran erinnern sollte, dass es null sein könnte , ähm, ich meine None), aber dies ist ein kleiner Vorteil im Austausch für ein zusätzliches Wrapper-Objekt .
Wo Option wirklich anfängt, seine Macht zu zeigen, hilft es Ihnen, mit dem Konzept von Ich-wollte-etwas-aber-ich-habe-nicht-tatsächlich-eins umzugehen.
Betrachten wir einige Dinge, die Sie möglicherweise mit Dingen tun möchten, die möglicherweise null sind.
Vielleicht möchten Sie einen Standardwert festlegen, wenn Sie eine Null haben. Vergleichen wir Java und Scala:
Anstelle eines etwas umständlichen ?: Konstrukts haben wir eine Methode, die sich mit der Idee befasst, "einen Standardwert zu verwenden, wenn ich null bin". Dies bereinigt Ihren Code ein wenig.
Vielleicht möchten Sie nur dann ein neues Objekt erstellen, wenn Sie einen echten Wert haben. Vergleichen Sie:
Scala ist etwas kürzer und vermeidet wiederum Fehlerquellen. Betrachten Sie dann den kumulativen Nutzen, wenn Sie Dinge miteinander verketten müssen, wie in den Beispielen von Synesso, Daniel und paradigmatisch gezeigt.
Es ist keine große Verbesserung, aber wenn Sie alles zusammenzählen, lohnt es sich überall, sehr leistungsstarken Code zu speichern (wo Sie selbst den winzigen Aufwand beim Erstellen des Some (x) -Wrapper-Objekts vermeiden möchten).
Die Verwendung von Übereinstimmungen ist für sich genommen nicht wirklich hilfreich, außer als Gerät, um Sie über den Fall null / keine zu informieren. Wenn es wirklich hilfreich ist, wenn Sie anfangen, es zu verketten, z. B. wenn Sie eine Liste von Optionen haben:
Jetzt können Sie die Fälle "Keine" und "Fälle ohne Liste" in einer praktischen Anweisung zusammenfassen, die genau den gewünschten Wert herausholt.
quelle
Null-Rückgabewerte sind nur aus Kompatibilitätsgründen mit Java vorhanden. Sie sollten sie nicht anderweitig verwenden.
quelle
Es ist wirklich eine Frage des Programmierstils. Wenn Sie Functional Java verwenden oder Ihre eigenen Hilfsmethoden schreiben, können Sie Ihre Option-Funktionalität nutzen, ohne die Java-Sprache aufzugeben:
http://functionaljava.org/examples/#Option.bind
Nur weil Scala es standardmäßig enthält, ist es nichts Besonderes. Die meisten Aspekte funktionaler Sprachen sind in dieser Bibliothek verfügbar und können gut mit anderem Java-Code koexistieren. So wie Sie Scala mit Nullen programmieren können, können Sie auch Java ohne Nullen programmieren.
quelle
Option gibt im Voraus zu, dass es sich um eine glatte Antwort handelt, und ist eine Monade.
quelle
Eigentlich teile ich den Zweifel mit Ihnen. Über Option stört es mich wirklich, dass 1) es einen Leistungsaufwand gibt, da es eine Menge von "einigen" Wrappern gibt, die überall erstellt werden. 2) Ich muss viel Some und Option in meinem Code verwenden.
Um die Vor- und Nachteile dieser Sprachentwurfsentscheidung zu erkennen, sollten wir Alternativen in Betracht ziehen. Da Java das Problem der Nullfähigkeit einfach ignoriert, ist es keine Alternative. Die eigentliche Alternative bietet die Programmiersprache Fantom. Es gibt dort nullbare und nicht nullbare Typen und ?. ?: Operatoren anstelle von Scalas map / flatMap / getOrElse. Im Vergleich sehe ich folgende Aufzählungszeichen:
Vorteil der Option:
Nullables Vorteil:
Es gibt hier also keinen offensichtlichen Gewinner. Und noch eine Anmerkung. Es gibt keinen syntaktischen Hauptvorteil für die Verwendung von Option. Sie können Folgendes definieren:
Oder verwenden Sie einige implizite Konvertierungen, um eine Pritty-Syntax mit Punkten zu erhalten.
quelle
Der eigentliche Vorteil expliziter Optionstypen besteht darin, dass Sie sie nicht an 98% aller Stellen verwenden können und somit Nullausnahmen statisch ausschließen. (Und bei den anderen 2% erinnert Sie das Typsystem daran, ordnungsgemäß zu überprüfen, wann Sie tatsächlich darauf zugreifen.)
quelle
Eine andere Situation, in der Option funktioniert, ist in Situationen, in denen Typen keinen Nullwert haben können. Es ist nicht möglich, Null in einem Int-, Float-, Double- usw. Wert zu speichern, aber mit einer Option können Sie None verwenden.
In Java müssten Sie die Box-Versionen (Integer, ...) dieser Typen verwenden.
quelle