Ich habe über die (Un) Bequemlichkeit gelesen, null
statt (zum Beispiel) zu haben Maybe
. Nachdem ich diesen Artikel gelesen habe , bin ich überzeugt, dass es viel besser wäre, ihn zu verwendenMaybe
(oder etwas Ähnliches). Ich bin jedoch überrascht zu sehen, dass alle "bekannten" imperativen oder objektorientierten Programmiersprachen weiterhin verwenden null
(was den ungeprüften Zugriff auf Typen ermöglicht, die einen Wert "nichts" darstellen können) und dass dies Maybe
hauptsächlich in funktionalen Programmiersprachen verwendet wird.
Betrachten Sie als Beispiel den folgenden C # -Code:
void doSomething(string username)
{
// Check that username is not null
// Do something
}
Hier riecht etwas schlecht ... Warum sollten wir prüfen, ob das Argument null ist? Sollten wir nicht annehmen, dass jede Variable einen Verweis auf ein Objekt enthält? Wie Sie sehen, besteht das Problem darin, dass per Definition fast alle Variablen eine Nullreferenz enthalten können. Was wäre, wenn wir entscheiden könnten , welche Variablen "nullbar" sind und welche nicht? Das erspart uns viel Mühe beim Debuggen und Suchen einer "NullReferenceException". Stellen Sie sich vor, dass standardmäßig keine Typen eine Nullreferenz enthalten könnten . Stattdessen würden Sie explizit angeben, dass eine Variable nur dann eine Nullreferenz enthalten kann , wenn Sie sie wirklich benötigen. Das ist die Idee hinter Vielleicht. Wenn Sie eine Funktion haben, die in einigen Fällen fehlschlägt (z. B. Division durch Null), können Sie a zurückgebenMaybe<int>
mit dem ausdrücklichen Hinweis, dass das Ergebnis ein int sein kann, aber auch nichts! Dies ist ein Grund, Vielleicht anstelle von Null zu bevorzugen . Wenn Sie an weiteren Beispielen interessiert sind, empfehle ich Ihnen, diesen Artikel zu lesen .
Die Tatsache ist, dass trotz der Nachteile, die es mit sich bringt, die meisten Typen standardmäßig auf null zu setzen, die meisten OO-Programmiersprachen dies tatsächlich tun. Deshalb frage ich mich über:
- Welche Art von Argumenten würden Sie haben zu implementieren ,
null
anstatt in Ihrer ProgrammierspracheMaybe
? Gibt es überhaupt Gründe oder handelt es sich nur um "historisches Gepäck"?
Bitte stellen Sie sicher, dass Sie den Unterschied zwischen null und Vielleicht verstehen, bevor Sie diese Frage beantworten.
null
es kein Konzept gibt (IIRC Haskell ist ein solches Beispiel).null
sie schon seit langer Zeit vorkommen. Es ist nicht einfach, das einfach fallen zu lassen.Antworten:
Ich glaube, es ist hauptsächlich historisches Gepäck.
Die bekannteste und älteste Sprache mit
null
ist C und C ++. Aber hiernull
macht das Sinn. Zeiger sind immer noch ein ziemlich numerisches und einfaches Konzept. Und wie jemand anderes sagte, ist es in der Denkweise von C- und C ++ - Programmierernnull
nicht sinnvoll , explizit zu sagen, dass Zeiger sein können .An zweiter Stelle kommt Java. Da Java-Entwickler versucht haben, C ++ am nächsten zu kommen, um den Übergang von C ++ zu Java zu vereinfachen, wollten sie sich wahrscheinlich nicht mit einem solchen Kernkonzept der Sprache anlegen. Außerdem
null
würde das Implementieren von explizit viel mehr Aufwand erfordern, da Sie nach der Initialisierung überprüfen müssen, ob die Nicht-Null-Referenz tatsächlich richtig eingestellt ist.Alle anderen Sprachen sind mit Java identisch. Sie kopieren normalerweise die Art und Weise, wie C ++ oder Java dies tun, und angesichts des Kernkonzepts impliziter
null
Verweistypen wird es sehr schwierig, eine Sprache zu entwerfen, die explizit verwendetnull
.quelle
null
wäre auch keine große Veränderung, denke ich.std::string
kann nicht seinnull
.int&
kann nicht seinnull
. Anint*
can und C ++ erlauben aus zwei Gründen den ungeprüften Zugriff darauf: 1. weil C dies getan hat und 2. weil Sie verstehen sollen, was Sie tun, wenn Sie in C ++ rohe Zeiger verwenden.Eigentlich
null
ist das eine tolle Idee. Bei einem gegebenen Zeiger möchten wir angeben, dass dieser Zeiger keinen gültigen Wert referenziert. Also nehmen wir einen Speicherort, erklären ihn für ungültig und halten uns an diese Konvention (eine Konvention, die manchmal mit Segfaults erzwungen wird). Wenn ich jetzt einen Zeiger habe, kann ich überprüfen, ob erNothing
(ptr == null
) oderSome(value)
(ptr != null
,value = *ptr
) enthält. Ich möchte, dass Sie verstehen, dass dies einemMaybe
Typ entspricht.Die Probleme dabei sind:
In vielen Sprachen hilft das Typensystem hier nicht, eine Nicht-Null-Referenz zu garantieren.
Dies ist historisches Gepäck, da viele gängige Imperativ- oder OOP-Sprachen im Vergleich zu ihren Vorgängern nur inkrementelle Fortschritte in ihren Typsystemen erzielt haben. Kleine Änderungen haben den Vorteil, dass neue Sprachen leichter zu lernen sind. C # ist eine Standardsprache, die Tools auf Sprachebene eingeführt hat, um mit Nullen besser umgehen zu können.
API-Designer kehren möglicherweise
null
bei einem Fehlschlag zurück, beziehen sich jedoch beim Erfolg nicht auf die eigentliche Sache. Oft wird die Sache (ohne Referenz) direkt zurückgegeben. Diese Abflachung einer Zeigerebene macht es unmöglich, sienull
als Wert zu verwenden.Dies ist nur eine Faulheit des Designers und kann nicht behoben werden, ohne eine ordnungsgemäße Verschachtelung mit einem ordnungsgemäßen Typsystem durchzusetzen. Einige Benutzer versuchen dies möglicherweise auch mit Überlegungen zur Leistung oder mit optionalen Überprüfungen zu rechtfertigen (eine Sammlung kann zurückgegeben werden
null
oder das Element selbst, stellt jedoch auch einecontains
Methode bereit ).In Haskell gibt es einen ordentlichen Blick auf den
Maybe
Typ als Monade. Dies erleichtert das Erstellen von Transformationen für den enthaltenen Wert.Auf der anderen Seite behandeln Low-Level-Sprachen wie C Arrays kaum als eigenen Typ, daher bin ich mir nicht sicher, was wir erwarten. In OOP-Sprachen mit parametrisiertem Polymorphismus ist ein zur Laufzeit überprüfter
Maybe
Typ eher trivial zu implementieren.quelle
null
weniger verwenden als in der Vergangenheit?null
Standardsprache, die Tools auf Sprachebene eingeführt hat, um mit s besser umgehen zu können " - ich spreche überNullable
Typen und den??
Standardoperator. Es löst das Problem im Moment nicht, wenn Legacy-Code vorhanden ist, aber es ist ein Schritt in Richtung einer besseren Zukunft.Nullable
.Mein Verständnis ist, dass dies
null
ein notwendiges Konstrukt war, um Programmiersprachen aus der Assemblierung herauszuziehen. 1 Programmierer mussten angeben können, dass ein Zeiger- oder Registerwert der gebräuchliche Begriff für diese Bedeutung warnot a valid value
undnull
wurde.Um den Punkt zu bekräftigen, der
null
nur eine Konvention ist, um ein Konzept darzustellen, kann der tatsächliche Wert, dernull
verwendet wird, je nach Programmiersprache und Plattform variieren.Wenn Sie eine neue Sprache entwerfen und diese vermeiden,
null
abermaybe
stattdessen verwenden möchten, würde ich einen aussagekräftigeren Begriff wienot a valid value
oder als Hinweisnavv
auf die Absicht empfehlen. Aber der Name des nicht-Wert ist ein separates Konzept aus , ob Sie sollten erlauben nicht-Werte auch in Ihrer Sprache vorhanden sind .Bevor Sie sich für einen dieser beiden Punkte entscheiden können, müssen Sie definieren, was die Bedeutung
maybe
für Ihr System bedeuten würde. Sie stellen möglicherweise fest, dass es sich nur um eine Umbenennung dernull
Bedeutung von handelt,not a valid value
oder Sie stellen möglicherweise fest, dass es eine andere Semantik für Ihre Sprache hat.Ebenso ist die Entscheidung, ob der Zugriff auf
null
Zugriff oder Verweis geprüft wird, eine andere Entwurfsentscheidung Ihrer Sprache.Um ein wenig Geschichte zu
C
erzählen, ging man implizit davon aus, dass Programmierer verstanden haben, was sie beim Manipulieren des Speichers zu tun versuchten. Da es sich um eine überlegene Abstraktion gegenüber der Assemblierung und den ihr vorausgegangenen Imperativsprachen handelte, war mir der Gedanke, den Programmierer vor einem falschen Verweis zu schützen, nicht in den Sinn gekommen.Ich glaube, dass einige Compiler oder ihre zusätzlichen Tools eine Möglichkeit bieten, den ungültigen Zeigerzugriff zu überprüfen. Andere haben dieses potenzielle Problem erkannt und Maßnahmen zum Schutz dagegen ergriffen.
Ob Sie dies zulassen sollten oder nicht, hängt davon ab, was Ihre Sprache leisten soll und welchen Grad an Verantwortung Sie den Nutzern Ihrer Sprache übertragen möchten. Es hängt auch von Ihrer Fähigkeit ab, einen Compiler zu erstellen, um diese Art von Verhalten einzuschränken.
So beantworten Sie Ihre Fragen:
"Welche Art von Argumenten ..." - Nun, es hängt davon ab, was die Sprache tun soll. Wenn Sie den Bare-Metal-Zugriff simulieren möchten, möchten Sie ihn möglicherweise zulassen.
"Ist es nur historisches Gepäck?" Vielleicht vielleicht auch nicht.
null
sicherlich hatte / hat Bedeutung für eine Reihe von Sprachen und hilft, den Ausdruck dieser Sprachen voranzutreiben. Der historische Präzedenzfall hat möglicherweise die neueren Sprachen und deren Zulassen beeinflusst,null
aber es ist ein bisschen viel, mit den Händen zu wedeln und das Konzept für nutzloses historisches Gepäck zu erklären.1 Siehe diesen Wikipedia-Artikel, obwohl Hoare für Nullwerte und objektorientierte Sprachen anerkannt ist. Ich glaube, die imperativen Sprachen haben sich in einem anderen Stammbaum entwickelt als Algol.
quelle
null
).object o = null; o.ToString();
für mich ohne Fehler oder Warnungen in VS2012 einwandfrei kompiliert. ReSharper beschwert sich darüber, aber das ist nicht der Compiler.Wenn Sie sich die Beispiele in dem Artikel ansehen, den Sie zitiert haben,
Maybe
verkürzt die meiste Zeit die Verwendung des Codes nicht. Dies macht eine Überprüfung nicht überflüssigNothing
. Der einzige Unterschied ist, dass Sie über das Typensystem daran erinnert werden, dies zu tun.Beachten Sie, ich sage "erinnern", nicht erzwingen. Programmierer sind faul. Wenn ein Programmierer davon überzeugt ist, dass ein Wert unmöglich sein kann
Nothing
, wird er den Wert dereferenzieren,Maybe
ohne ihn zu überprüfen, genau wie er jetzt einen Nullzeiger dereferenziert. Das Endergebnis ist, dass Sie eine Nullzeigerausnahme in eine "dereferenzierte leere Vielleicht" -Ausnahme konvertieren.Dasselbe Prinzip der menschlichen Natur gilt auch in anderen Bereichen, in denen Programmiersprachen versuchen, Programmierer zu etwas zu zwingen. Zum Beispiel versuchten die Java-Designer, die meisten Ausnahmen zu verarbeiten, was zu einer großen Anzahl von Ausnahmen führte, die entweder unbemerkt ignoriert oder blindlings weitergegeben wurden.
Was macht,
Maybe
ist schön, wenn viele Entscheidungen über Pattern Matching und Polymorphismus statt expliziter Prüfungen getroffen werden. Sie könnten beispielsweise separate Funktionen erstellenprocessData(Some<T>)
undprocessData(Nothing<T>)
, mit denen Sie nichts anfangen könnennull
. Sie verschieben Ihre Fehlerbehandlung automatisch in eine separate Funktion, was bei der funktionalen Programmierung sehr wünschenswert ist, wenn Funktionen nicht immer von oben nach unten aufgerufen werden, sondern nur schleppend weitergegeben und ausgewertet werden. In OOP ist die bevorzugte Methode zum Entkoppeln des Fehlerbehandlungscodes mit Ausnahmen.quelle
const
, aber optional zu machen. Einige Arten von Code auf niedrigerer Ebene, wie beispielsweise verknüpfte Listen, wären bei der Implementierung mit nicht nullwertfähigen Objekten sehr ärgerlich.map :: Maybe a -> (a -> b)
undbind :: Maybe a -> (a -> Maybe b)
Definitionen darauf enthalten sein, damit Sie mit der Neufassung mit einer if else-Anweisung weitere Berechnungen fortsetzen und einfädeln können. UndgetValueOrDefault :: Maybe a -> (() -> a) -> a
ermöglicht es Ihnen, den nullbaren Fall zu behandeln. Es ist weitaus eleganter als derMaybe a
explizite Mustervergleich .Maybe
ist eine sehr funktionale Denkweise für ein Problem - es gibt etwas, und es kann einen definierten Wert haben oder nicht. Im objektorientierten Sinne ersetzen wir jedoch die Idee eines Dings (egal ob es einen Wert hat oder nicht) durch ein Objekt. Ein Objekt hat eindeutig einen Wert. Wenn nicht, sagen wir, das Objekt istnull
, aber wir meinen wirklich, dass es überhaupt kein Objekt gibt. Der Verweis, den wir auf das Objekt haben, zeigt auf nichts. Die ÜbersetzungMaybe
in ein OO-Konzept ist nichts Neues - in der Tat sorgt es nur für eine größere Code-Unordnung. Sie müssen immer noch eine Nullreferenz für den Wert von habenMaybe<T>
. Sie müssen immer noch Nullprüfungen durchführen (in der Tat müssen Sie viel mehr Nullprüfungen durchführen, was Ihren Code überfrachtet), auch wenn sie jetzt "Vielleichtprüfungen" heißen. Sicher, Sie werden robusteren Code schreiben, wie der Autor behauptet, aber ich würde argumentieren, dass dies nur der Fall ist, weil Sie die Sprache weitaus abstrakter und stumpfer gemacht haben, was in den meisten Fällen einen unnötigen Arbeitsaufwand erfordert. Ich mache ab und zu gerne eine NullReferenceException, bevor ich mich mit Spaghetti-Code beschäftige, der bei jedem Zugriff auf eine neue Variable eine Vielleicht-Prüfung durchführt.quelle
Maybe<T>
mussnull
als Wert erlauben , da der Wert möglicherweise nicht existiert. Wenn ich habeMaybe<MyClass>
und es keinen Wert hat, muss das Wertfeld eine Nullreferenz enthalten. Es gibt nichts anderes, was nachweislich sicher ist.Maybe
könnte es sich um eine abstrakte Klasse handeln, von der zwei Klassen erben:Some
(die ein Feld für den Wert enthält) undNone
(die dieses Feld nicht enthält). Auf diese Weise ist der Wert nienull
.Das Konzept von
null
lässt sich leicht auf C zurückführen, aber hier liegt nicht das Problem.Meine Alltagssprache ist C # und ich würde
null
mit einem Unterschied bleiben . C # hat zwei Arten von Typen, Werten und Referenzen . Werte können niemals seinnull
, aber es gibt Zeiten, in denen ich gerne ausdrücken möchte, dass kein Wert vollkommen in Ordnung ist. Um dies zu tun, verwendet C #Nullable
Typen, alsoint
den Wert undint?
den nullbaren Wert. So sollten meiner Meinung nach auch Referenztypen funktionieren.Siehe auch: Nullreferenz darf kein Fehler sein :
quelle
Ich denke, das liegt daran, dass sich die funktionale Programmierung sehr um Typen kümmert , insbesondere um Typen, die andere Typen (Tupel, Funktionen als erstklassige Typen, Monaden usw.) kombinieren als die objektorientierte Programmierung (oder zumindest anfangs).
Moderne Versionen der Programmiersprachen (C ++, C #, Java) basieren alle auf Sprachen, die keine generische Programmierung hatten (C, C # 1.0, Java 1). Ohne das können Sie immer noch einen Unterschied zwischen nullbaren und nicht nullbaren Objekten in die Sprache schreiben (wie C ++ - Referenzen, die nicht möglich
null
, aber auch begrenzt sind), aber es ist viel weniger natürlich.quelle
Ich denke, der Hauptgrund ist, dass relativ wenige Nullprüfungen erforderlich sind, um ein Programm "sicher" gegen Datenkorruption zu machen. Wenn ein Programm versucht, den Inhalt eines Array-Elements oder eines anderen Speicherorts zu verwenden, der mit einer gültigen Referenz geschrieben sein soll, dies aber nicht war, wird im besten Fall eine Ausnahme ausgelöst. Im Idealfall gibt die Ausnahme genau an, wo das Problem aufgetreten ist. Entscheidend ist jedoch, dass eine Art Ausnahme ausgelöst wird, bevor die Nullreferenz an einer Stelle gespeichert wird, die zu einer Beschädigung der Daten führen kann. Wenn eine Methode ein Objekt nicht speichert, ohne es zuerst in irgendeiner Weise zu verwenden, stellt der Versuch, ein Objekt zu verwenden, an und für sich eine Art "Nullprüfung" dar.
Wenn sichergestellt werden soll, dass eine Nullreferenz, die an der Stelle angezeigt wird, an der sie nicht auftreten sollte, eine andere Ausnahme hervorruft als
NullReferenceException
, müssen häufig überall Nullprüfungen eingefügt werden. Auf der anderen Seite erfordert das bloße Sicherstellen, dass eine Ausnahme auftritt, bevor eine Nullreferenz einen "Schaden" hervorruft, der über die bereits durchgeführten hinausgeht, häufig relativ wenige Tests - Tests wären im Allgemeinen nur in Fällen erforderlich, in denen ein Objekt eine Verweis, ohne zu versuchen, ihn zu verwenden, und entweder würde der Null-Verweis einen gültigen überschreiben, oder es würde dazu führen, dass anderer Code andere Aspekte des Programmstatus falsch interpretiert. Solche Situationen existieren, sind aber nicht allzu häufig. Die meisten versehentlichen Nullreferenzen werden sehr schnell abgefangenob man nach ihnen sucht oder nicht .quelle
"
Maybe
," wie geschrieben, ist ein höheres Konstrukt als null. Wenn Sie mehr Wörter verwenden, um es zu definieren, lautet es möglicherweise: "Ein Zeiger auf eine Sache oder ein Zeiger auf nichts, aber der Compiler hat noch nicht genügend Informationen erhalten, um zu bestimmen, welche." Dies zwingt Sie dazu, jeden Wert ständig explizit zu überprüfen, es sei denn, Sie erstellen eine Compilerspezifikation, die intelligent genug ist, um mit dem von Ihnen geschriebenen Code Schritt zu halten.Sie können eine Implementierung von "Vielleicht" mit einer Sprache vornehmen, die leicht Nullen enthält. C ++ hat eine in Form von
boost::optional<T>
. Das Äquivalent von null mit Vielleicht ist sehr schwierig. Insbesondere wenn ich a habeMaybe<Just<T>>
, kann ich es nicht null zuweisen (da ein solches Konzept nicht existiert), während esT**
in einer Sprache mit null sehr einfach ist, null zuzuweisen. Dies erzwingt die Verwendung einesMaybe<Maybe<T>>
Objekts, was völlig gültig ist. Sie werden jedoch dazu gezwungen, weitere Überprüfungen durchzuführen, um dieses Objekt zu verwenden.Einige funktionale Sprachen verwenden Vielleicht, weil null entweder undefiniertes Verhalten oder Ausnahmebehandlung erfordert, was kein einfaches Konzept ist, um Syntaxen für funktionale Sprachen zuzuordnen. Vielleicht füllt die Rolle in solchen funktionalen Situationen viel besser aus, aber in prozeduralen Sprachen ist null König. Es geht nicht um Richtig und Falsch, sondern nur darum, was es einfacher macht, dem Computer zu sagen, was er tun soll.
quelle