Ich bin ein absoluter Neuling bei OCaml. Ich bin kürzlich auf diese Seite gestoßen, die eine Menge Kritik gegenüber OCaml auflistet.
Zu sehen, dass die Seite schon ziemlich alt ist (2007): Welche der dort aufgelisteten Aufzählungspunkte sind heute noch wahr? Zum Beispiel: Stimmt es immer noch, dass es unmöglich ist, ein generisches Objekt zu drucken?
Ich möchte klarstellen, dass ich keine Diskussion der darin geäußerten Meinungen suche. Ich frage, ob die aufgelisteten Informationen, z. B. die Tatsache, dass Ganzzahlen ohne Warnungen überlaufen, für neuere Versionen von OCaml noch korrekt sind
Antworten:
Dieser Artikel wird an mehreren Stellen besprochen:
Zusammenfassend: Ja, OCaml ist kein Lisp und nein, es ist nicht perfekt (was bedeutet das?). Ich denke nicht, dass die Punkte, die in diesem Blogbeitrag erwähnt werden, für alltägliche O'Caml-Programmierer relevant sind.
Nach dem Studium von O'Caml halte ich es für eine interessante Sprache, die Ihnen beim Erstellen von Programmen helfen kann, die Sie nicht einmal wagen würden, beispielsweise in C / C ++ / Java zu schreiben: Schauen Sie sich zum Beispiel Frama-C an .
Um eine aktuelle Beschreibung von O'Caml zu erhalten, empfehlen wir Ihnen, sich mit seinen Funktionen vertraut zu machen: Die Sprache unterstützt starke Techniken zur statischen Typprüfung, mit denen sich Implementierungen auf die Erstellung performanter und dennoch sicherer Laufzeiten konzentrieren können.
Wichtig : Ich bin kein OCaml-Experte: Wenn Sie einer von ihnen sind und sehen, dass ich etwas fürchterlich Falsches geschrieben habe, korrigieren Sie mich bitte. Ich werde diesen Beitrag entsprechend bearbeiten.
Statische Typprüfung
Falsches Gefühl der Sicherheit
Das ist wahr, aber offensichtlich.
Durch die statische Eingabe erhalten Sie Beweise, denen Sie in Bezug auf eine Teilmenge der Eigenschaften Ihres Programms vertrauen können. Wenn Sie nicht akzeptieren, dass Sie alle formellen Anforderungen erfüllen, kann ein durchschnittliches Programm (das kein Spielzeug ist) Programmierfehler aufweisen, die nur zur Laufzeit auftreten können.
Dann können dynamische Prüftechniken angewendet werden: Der OCaml-Compiler verfügt über Flags zum Generieren von ausführbaren Dateien mit Debuginformationen usw. Oder er kann Code generieren, der dem Programmierer blind vertraut und Typinformationen so weit wie möglich löscht. Programmierer, die robuste Programme wünschen, sollten dynamische Prüfungen explizit implementieren.
Das Gleiche gilt zB für Common Lisp, aber umgekehrt: Dynamische Typen zuerst, mit optionalen Typdeklarationen und Compiler-Direktiven an zweiter Stelle.
Einige Grundtypen
Es gilt weiterhin: Die Hauptsprache hat sich nicht (oder nicht dramatisch) geändert.
Silent Integer Overflow
Dies ist die Norm in den meisten Sprachen, in denen ein Ganzzahlüberlauf von Hand überprüft wird. Ich kenne keine Bibliothek, die Operationen überprüft, um festzustellen, ob ein Überlauf auftreten kann.
Modul Unveränderlichkeit
Der Autor erwähnt Functors, aber ich verstehe nicht, wie sein Beispiel nicht umgesetzt werden kann. Beim Lesen des Kapitels First Class Modules von https://realworldocaml.org scheint es, dass Module verwendet werden können , um neue Module zu komponieren und zu erstellen . Natürlich erfordert das Ändern eines vorhandenen Moduls eine Änderung des Quellcodes, aber auch dies ist unter Programmiersprachen nicht ungewöhnlich.
" Funktionen werden semantisch INLINE übersetzt"
Der obige reddit-Thread ist anderer Meinung und besagt, dass die Bindung zum Zeitpunkt des Links aufgelöst wird. Dies ist jedoch ein Implementierungsdetail, und ich denke, dass sich das Hervorheben semantisch auf die Art und Weise bezieht, wie Funktionen aufgelöst werden. Beispiel:
Das obige Programm kompiliert und gibt bei Ausführung 5 zurück, da
g
es mit der ersten Version von definiert istf
, als ob die aufrufende Funktiong
den Aufruf von inliniert hättef
. Dies ist übrigens nicht "schlecht", sondern entspricht lediglich O'Camls Namensschattierungsregeln.Zusammenfassend : Ja, Module sind unveränderlich . Sie sind aber auch zusammensetzbar .
Polymorphismus verursacht Laufzeitfehler
Ich kann den genannten Fehler nicht reproduzieren. Ich vermute, dass es sich um einen Compilerfehler handelt.
Keine Makros
In der Tat gibt es keine Makros, sondern Präprozessoren (OcamlP4, OcamlP5, ...).
Wrapper (mit geöffneter Datei)
Zu wissen, wie nützlich UNWIND-PROTECT sein kann, ist in der Tat schmerzhaft, wenn es in mehr Sprachen fehlt. Hier sind einige Optionen:
Setzt
Ich glaube nicht, dass es in OCaml verallgemeinerte Referenzen gibt.
Geringfügige Sprachlosigkeit
Rekord Feldnamen Hölle
Richtig, aber Sie sollten Module verwenden:
Syntax
Gilt immer noch (aber das ist wirklich nur Syntax).
Kein Polymorphismus
Gilt immer noch, aber irgendwie gibt es Leute, die das anstelle von Lisps numerischem Turm bevorzugen (ich weiß nicht warum). Ich nehme an, es hilft bei der Typinferenz.
Inkonsistente Funktionssätze
Siehe das OCaml Batteries Included- Projekt. Insbesondere BatArray als Beispiel
map2
für Arrays.Keine dynamischen Variablen
Kann implementiert werden:
Optionale ~ Argumente saugen
Aufgrund von Spracheinschränkungen können Sie in Common Lisp keine optionalen Argumente und Schlüsselwortargumente mischen. Heißt das, es ist scheiße? (Natürlich kann dies mit Makros geändert werden (siehe zB meine Antwort )). Siehe O'Caml Dokumentation für optionale und benannte Argumente in O'Caml.
Teilweise Inkonsistenz der Argumentanwendung
Ich denke nicht, dass das in der Praxis wirklich ärgerlich ist.
Lesbarkeit der Arithmetik
Es gilt, aber Sie können R oder Python für numerische Probleme verwenden, wenn Sie dies vorziehen.
Stille Namenskonfliktlösung
Gilt immer noch, aber beachten Sie, dass dies gut dokumentiert ist.
Keine Objekteingabe / -ausgabe
Gilt immer noch.
Implementierung, Bibliotheken
Diese ändern sich jeden Tag: Es gibt keine endgültige Antwort.
Schließlich,
... gilt immer noch.
quelle
Falsches Gefühl der Sicherheit . Das ist Unsinn.
Einige Grundtypen . OCaml verfügt nun über Bytes und Bytearrays, jedoch keine integrierten Unicode-Zeichenfolgen, 16-Bit-Ganzzahlen, vorzeichenlosen Ganzzahlen, 32-Bit-Gleitkommazahlen, Vektoren oder Matrizen. Bibliotheken von Drittanbietern bieten einige davon an.
Silent Integer Overflow . Unverändert, aber es war nie ein Problem.
Modul Unveränderlichkeit . Seine Empfehlung, dass Funktionen und Module wandelbar sein sollten, ist ein grimmiger Rückfall für Lisp und eine wirklich schlechte Idee. Sie können Module mit ersetzen,
include
wenn Sie möchten, aber Sie können sie natürlich nicht mutieren.Polymorphismus verursacht Laufzeitfehler . Dies ist ein großes Problem mit OCaml und es wurde nicht behoben. Während sich Ihre Typen zu einer polymorphen Gleichheit entwickeln, werden Vergleich und Hashing fehlschlagen, wenn sie auf Typen wie Funktionen stoßen und das Problem nur sehr schwer behoben werden kann. F # hat eine großartige Lösung für dieses Problem.
Keine Makros . Ironischerweise hatte OCaml, als er dies schrieb, tatsächlich die volle Unterstützung für Makros, aber sie haben sich jetzt entschieden, die Funktion herauszunehmen.
Wrapper . Dies war ein echtes Problem und es wurde nicht behoben. Es gibt noch kein
try ... finally
Konstrukt in der OCaml-Sprache und keinen Wrapper, der es in der stdlib implementiert.Orte . Unverändert, aber kein Problem.
Rekord Feldnamen Hölle . Strukturieren Sie Ihren Code richtig mit Modulen.
Syntax . Unverändert, aber kein Problem.
Kein Polymorphismus . Dies war größtenteils Unsinn, als er es schrieb und nichts hat sich geändert.
Inkonsistente Funktionssätze . OCaml hat noch keine
cons
Funktion. Das ist gut. Ich möchte kein Lisp-Zeug in meiner Sprache, danke.Keine dynamischen Variablen . War eine gute Sache über OCaml. Ist immer noch eine gute Sache über OCaml.
Optionale ~ Argumente saugen . Optionale Argumente rocken. Ich habe Microsoft verärgert, dass sie optionale Argumente zu F # hinzufügen sollen.
Teilweise Inkonsistenz der Argumentanwendung . Wie?
Lesbarkeit der Arithmetik . Dies hat sich geändert, seit ich OCaml ~ vor 8 Jahren nicht mehr benutze. Anscheinend kannst du es jetzt tun
Int64.((q * n - s * s) / (n - 1L))
.Stille Namenskonfliktlösung . Er hat versucht, in der REPL eine vollständige Softwareentwicklung durchzuführen, wie Sie es in Lisp tun würden. Mach das nicht in OCaml. Verwenden Sie Dateien und Stapelkompilierungen, die auf die REPL zurückgreifen, nur zum Testen, Ausführen von Einwegcode und für interaktives technisches Rechnen.
Reihenfolge der Bewertung . Das war falsch, als er es schrieb. Die Reihenfolge der Auswertung ist in OCaml undefiniert.
Keine Objekteingabe / -ausgabe . Er zitierte eine Drittanbieter-Bibliothek, die dieses "Problem" bereits löste.
Der Compiler stoppt nach dem ersten Fehler . Wie?
Kein Stack-Trace für nativ kompilierte ausführbare Dateien . Fest.
Debugger saugt . Ich habe den Debugger nie benutzt. Die statische Typprüfung fängt fast alle meine Fehler ab.
GC saugt . Ich fand OCamls GC hervorragend, mit Ausnahme eines Hauptproblems: Die globale Sperre verhindert die parallele Programmierung.
Keine impliziten Vorwärtserklärungen . Die gegenseitige Rekursion ist in allen MLs ausdrücklich vorgesehen. Die einzige Kuriosität ist, dass
type
Definitionen standardmäßig rekursiv sind, währendlet
Bindungen standardmäßig nicht rekursiv sind.Funktionsrunde fehlt . OCaml hat immer noch eine reine Standardbibliothek, aber Bibliotheken von Drittanbietern wie Jane St's Core
round
und Freunde.Listen .
List.map
ist immer noch nicht rekursiv. Ich reichte Patches ein, um solche schwerwiegenden Fehler zu beheben, und musste Jahre warten, bis sie in Veröffentlichungen auftauchten. Listen sind natürlich noch unveränderlich. Und so sollten sie sein.Geschwindigkeit . Ich glaube, die Kompilierungszeiten für große polymorphe Varianten wurden behoben.
Mustervergleich . Ein Triumph der Hoffnung über die Realität. Die Lisp-Community hat dies versäumt. Daher meine 10. Regel: Jedes ausreichend komplizierte Lisp-Programm enthält eine ad-hoc, informell spezifizierte und fehlerbehaftete Implementierung der Hälfte von OCamls Pattern Match Compiler.
Als er schrieb, dass man es nicht einfach machen kann:
Sie können den hübschen Drucker jedoch von der obersten Ebene aus als Bibliotheksaufruf aufrufen und ihm die erforderlichen Typinformationen geben. Und es gab ein Makro, mit dem Sie Datenstrukturen mit Anmerkungen versehen können, damit hübsche Drucker automatisch generiert werden.
quelle
cons
einen schlechten Ton handelt.+
Ints, Floats und Komplexe verwenden, aber Sie können auch Ihre eigenen Typen definieren und eine Überladung hinzufügen+
, um an Ihrem Typ zu arbeiten. Dies bietet der Kürze und Lesbarkeit von Lisp oder Haskell die vorhersehbar gute Leistung von SML oder OCaml und erreicht etwas, was keine andere Sprache kann.