Sowohl bei StackOverflow als auch auf dieser Site mangelt es nicht an vagen "Scheme vs Common Lisp" -Fragen. Ich möchte diese daher noch genauer fokussieren. Die Frage richtet sich an Personen, die in beiden Sprachen codiert haben:
Welche spezifischen Elemente Ihrer Common-Lisp-Codierungserfahrung haben Sie beim Codieren in Scheme am meisten vermisst? Oder umgekehrt: Was haben Sie beim Codieren in Common Lisp beim Codieren in Scheme verpasst?
Ich meine nicht unbedingt nur Sprachfunktionen. Was die Frage betrifft, sind alle folgenden Punkte zu übersehen:
- Spezifische Bibliotheken.
- Besonderheiten von Entwicklungsumgebungen wie SLIME, DrRacket etc.
- Funktionen bestimmter Implementierungen, wie beispielsweise die Fähigkeit von Gambit, C-Code-Blöcke direkt in Ihre Scheme-Quelle zu schreiben.
- Und natürlich Sprachfunktionen.
Beispiele für die Art von Antworten, auf die ich hoffe:
- "Ich habe versucht, X in Common Lisp zu implementieren, und wenn ich die erstklassigen Fortsetzungen von Scheme gehabt hätte, hätte ich einfach nur Y gemacht, aber stattdessen musste ich Z machen, was mehr ein Schmerz war."
- "Das Skripting des Erstellungsprozesses in meinem Scheme-Projekt wurde immer schmerzhafter, als mein Quellbaum wuchs und ich immer mehr C-Bibliotheken verknüpfte. Für mein nächstes Projekt bin ich zu Common Lisp zurückgekehrt."
- "Ich habe eine große C ++ - Codebasis, und für mich war die Möglichkeit, C ++ - Aufrufe direkt in meinen Gambit-Schema-Code einzubetten, alle Mängel wert, die das Schema gegenüber Common Lisp aufweisen kann, auch wenn es keine SWIG-Unterstützung gibt."
Ich hoffe also auf Kriegsgeschichten und nicht auf allgemeine Gefühle wie "Schema ist eine einfachere Sprache" usw.
Antworten:
Mein Grundstudium war Kognitionswissenschaft und Künstliche Intelligenz. Von da an hatte ich eine Ein-Gänge-Einführung in Lisp. Ich fand die Sprache interessant (wie in "elegant"), dachte aber nicht wirklich darüber nach, bis ich viel später auf Greenspuns Zehnte Regel stieß:
Greenspuns Argument war (teilweise), dass in vielen komplexen Programmen Interpreter eingebaut sind. Anstatt einen Interpreter in eine Sprache zu integrieren, schlug er vor, dass es sinnvoller sein könnte, eine Sprache wie Lisp zu verwenden, in der bereits ein Interpreter (oder Compiler) integriert ist.
Zu der Zeit arbeitete ich an einer ziemlich großen App, die benutzerdefinierte Berechnungen mit einem benutzerdefinierten Interpreter für eine benutzerdefinierte Sprache durchführte. Ich beschloss, seinen Kern in Lisp als großes Experiment neu zu schreiben.
Es dauerte ungefähr sechs Wochen. Der ursprüngliche Code war ~ 100.000 Zeilen Delphi (eine Pascal-Variante). In Lisp wurde das auf ~ 10.000 Zeilen reduziert. Noch überraschender war jedoch die Tatsache, dass die Lisp-Engine 3-6-mal schneller war. Und denken Sie daran, dass dies die Arbeit eines Lisp-Neulings war! Diese ganze Erfahrung hat mir die Augen geöffnet. Zum ersten Mal sah ich die Möglichkeit, Leistung und Ausdruckskraft in einer einzigen Sprache zu verbinden.
Einige Zeit später, als ich anfing, an einem webbasierten Projekt zu arbeiten, sprach ich eine Reihe von Sprachen vor. Ich habe Lisp und Scheme in die Mischung aufgenommen. Am Ende habe ich eine Schema-Implementierung ausgewählt - Chez-Schema . Ich bin sehr zufrieden mit dem Ergebnis.
Das webbasierte Projekt ist eine leistungsstarke "Auswahlmaschine" . Wir verwenden Scheme auf verschiedene Arten, von der Verarbeitung von Daten über die Abfrage von Daten bis zur Seitenerstellung. An vielen Stellen haben wir tatsächlich mit einer anderen Sprache begonnen, sind aber aus Gründen, die ich im Folgenden kurz beschreiben werde, zu Scheme gewechselt.
Jetzt kann ich Ihre Frage (zumindest teilweise) beantworten.
Während der Audition haben wir uns verschiedene Lisp- und Scheme-Implementierungen angesehen. Auf der Lisp-Seite haben wir uns (glaube ich) Allegro CL, CMUCL, SBCL und LispWorks angesehen. Auf der Schemaseite haben wir uns (glaube ich) Bigloo, Chicken, Chez, Gambit angesehen. (Die Sprachauswahl ist schon lange her. Deshalb bin ich ein bisschen dunstig. Ich kann ein paar Notizen ausgraben, wenn es wichtig ist.)
Von Anfang an suchten wir nach a) nativen Threads und b) Unterstützung für Linux, Mac und Windows. Diese beiden Bedingungen zusammen haben alle außer (ich denke) Allegro und Chez umgehauen. Um die Bewertung fortzusetzen, mussten wir die Multithreading-Anforderungen lockern.
Wir haben eine Reihe kleiner Programme zusammengestellt und diese für Evaluierungen und Tests verwendet. Das ergab eine Reihe von Problemen. Beispiel: Einige Implementierungen wiesen Fehler auf, die verhinderten, dass einige Tests vollständig ausgeführt werden konnten. Einige Implementierungen konnten zur Laufzeit keinen Code kompilieren. Einige Implementierungen konnten zur Laufzeit kompilierten Code nicht einfach in vorkompilierten Code integrieren. Einige Implementierungen hatten Müllsammler, die deutlich besser (oder deutlich schlechter) waren als die anderen. usw.
Für unsere Bedürfnisse haben nur die drei kommerziellen Implementierungen - Allegro, Chez und Lispworks - unsere primären Tests bestanden. Von den dreien bestand nur Chez alle Tests mit Bravour. Zu der Zeit, glaube ich, hatte Lispworks auf keiner Plattform native Threads (ich glaube, das tun sie jetzt), und ich glaube, Allegro hatte auf einigen Plattformen nur native Threads. Darüber hinaus hatte Allegro eine Laufzeitlizenzgebühr, die mir nicht sonderlich gut gefallen hat. Ich glaube, Lispworks hatte keine Laufzeitgebühr und Chez hatte eine unkomplizierte (und sehr vernünftige) Vereinbarung (und diese trat nur in Kraft, wenn Sie den Compiler zur Laufzeit verwendeten).
Hier sind einige Vergleichs- und Kontrastpunkte, die sowohl in Lisp als auch in Scheme etwas bedeutende Codestücke hervorgebracht haben:
Die Lisp-Umgebungen sind weitaus ausgereifter. Sie bekommen viel mehr fürs Geld. (Allerdings bedeutet mehr Code auch mehr Fehler.)
Die Lisp-Umgebungen sind weitaus schwieriger zu erlernen. Sie brauchen viel mehr Zeit, um kompetent zu werden. Common Lisp ist eine riesige Sprache - und das ist, bevor Sie zu den Bibliotheken kommen, die die kommerziellen Implementierungen hinzufügen. (Trotzdem ist der Syntaxfall von Scheme weitaus subtiler und komplizierter als alles, was in Lisp vorkommt.)
In den Lisp-Umgebungen kann es etwas schwieriger sein, Binärdateien zu erstellen. Sie müssen Ihr Image "schütteln", um nicht benötigte Teile zu entfernen. Wenn Sie Ihr Programm während dieses Vorgangs nicht korrekt ausführen, können später Laufzeitfehler auftreten . Im Gegensatz dazu kompilieren wir mit Chez eine Datei der obersten Ebene, die alle anderen benötigten Dateien enthält, und wir sind fertig.
Ich sagte zuvor, dass wir Scheme an einer Reihe von Orten eingesetzt haben, die wir ursprünglich nicht beabsichtigt hatten. Warum? Mir fallen drei Gründe ein.
Zuerst haben wir gelernt, Chez (und seinem Entwickler Cadence) zu vertrauen. Wir haben viel vom Tool verlangt und es lieferte konsequent. Zum Beispiel hatte Chez in der Vergangenheit nur eine geringe Anzahl von Fehlern, und sein Speichermanager war sehr, sehr gut.
Zweitens haben wir gelernt, die Leistung von Chez zu lieben. Wir haben etwas verwendet, das sich wie eine Skriptsprache anfühlte - und wir haben daraus die Geschwindigkeit des nativen Codes gewonnen. Für einige Dinge, die keine Rolle spielten - aber es tat nie weh, und manchmal half es sehr viel.
Drittens haben wir gelernt, die Abstraktion zu lieben, die das Schema bieten kann. Ich meine übrigens nicht nur Makros; Ich meine Dinge wie Verschlüsse, Lambdas, Tail-Calls usw. Sobald Sie anfangen, in diesen Begriffen zu denken, scheinen andere Sprachen im Vergleich eher begrenzt zu sein.
Ist das Schema perfekt? Nein; Es ist ein Kompromiss. Erstens können einzelne Entwickler effektiver arbeiten - für Entwickler ist es jedoch schwieriger, sich gegenseitig den Code zu entlocken, da die Wegweiser der meisten Sprachen (z. B. für Schleifen) im Schema fehlen (z. B. gibt es eine Million Möglichkeiten) eine for-Schleife). Zweitens gibt es einen viel kleineren Pool von Entwicklern, mit denen man reden, die man anheuern, ausleihen usw. kann.
Zusammenfassend würde ich sagen: Lisp und Scheme bieten einige Funktionen, die nirgendwo sonst verfügbar sind. Diese Fähigkeit ist ein Kompromiss, also sollte es in Ihrem speziellen Fall sinnvoller sein. In unserem Fall hatten die entscheidenden Faktoren für die Entscheidung für Lisp oder Scheme mehr mit sehr grundlegenden Funktionen (Plattformunterstützung, Plattformthreads, Laufzeitkompilierung, Laufzeitlizenzierung) zu tun als mit Sprach- oder Bibliotheksfunktionen. Auch in unserem Fall war dies ein Kompromiss: Mit Chez haben wir die Kernfunktionen erhalten, die wir wollten, aber wir haben die umfangreichen Bibliotheken verloren, die die kommerziellen Lisp-Umgebungen hatten.
Um es noch einmal zu wiederholen: Wir haben uns vor langer Zeit die verschiedenen Lisps und Schemata angesehen. Sie haben sich alle seitdem weiterentwickelt und verbessert.
quelle
Normalerweise mag ich es nicht, einen Link als Antwort einzufügen, aber ich habe genau zu diesem Thema einen Blog-Artikel geschrieben. Es ist nicht erschöpfend, bringt aber einige der wichtigsten Punkte auf den Punkt.
http://symbo1ics.com/blog/?p=729
Edit : Hier sind die wichtigsten Punkte:
TERPRI
,PROGN
Etc. Schema hat in der Regel sehr vernünftige Namen. Dies ist etwas in CL verpasst.list
" bis "lst
" im Schema verschleiern .syntax-rules
ist alles in Ordnung und gut, bis du wirklich ein paar Dinge raushacken willst. Andererseits werden hygienische Makros in CL manchmal übersehen. Keine Standardmethode zu haben, bedeutet, das Rad neu zu erfinden.Während ich oben nur in der ersten Person gesprochen habe, sollte klar sein, was ich vermisse und was nicht.
[Ich entschuldige mich, wenn diese zu allgemein sind. Es scheint, dass Sie viel spezifischere Details wünschen können. Es gibt einige Besonderheiten in der Post.]
quelle
Ich habe kürzlich ein Home-Projekt mit einer Bibliothek gestartet, die eine C-Version und eine Java-Version hat. Ich wollte Lisp für das Projekt verwenden und verbrachte ungefähr einen Monat damit, zwischen der Verwendung von Common Lisp, Scheme oder Clojure zu schwanken. Ich habe einige Erfahrungen mit allen drei, aber nur mit Spielzeugprojekten. Ich erzähle Ihnen ein wenig über meine Erfahrungen mit jedem von ihnen, bevor ich Ihnen sage, für welches ich mich entschieden habe.
PLT Racket hat eine nette IDE, mit der Sie nicht nur Ausdrücke aus dem Editor auswerten, sondern auch Klammern anstelle von Parens eingeben und diese gegebenenfalls wieder auf Parens zurücksetzen können. Racket hat auch eine große Anzahl von Bibliotheken mit der Installation und noch mehr zum Download zur Verfügung. Der visuelle Debugger ist ebenfalls hilfreich.
Meine Common Lisp-Implementierung (SBCL) hat keine IDE, aber es ist bei Open-Source-CL-Implementierungen üblich, Emacs und SLIME zu verwenden. Diese Kombination kann sehr effizient sein. Neben der Möglichkeit, Ausdrücke während der Eingabe in die Quelldatei auszuwerten, gibt es auch eine REPL, die alle Bearbeitungsbefehle von Emacs enthält, sodass das Kopieren von Code effizient in beide Richtungen erfolgen kann. Sogar Objekte, die im REPL-Puffer angezeigt werden, können kopiert und eingefügt werden.
Alt+(
undAlt+)
sind effizient für den Umgang mit übereinstimmenden Klammern und Einrückungen.Alle oben genannten Emacs-Funktionen sind auch für Clojure verfügbar. Meine Bearbeitungserfahrung mit Clojure ähnelt der von Lisp. Das Java-Interop hat gut funktioniert, und ich möchte ein Clojure-Projekt erstellen, sobald es ausgereift ist.
Ich konnte mit allen drei (Common Lisp, Racket und Clojure) auf die Bibliothek zugreifen, entschied mich jedoch für Common Lisp für das Projekt. Ausschlaggebend war, dass das FFI in Common Lisp viel einfacher zu verwenden war. CFFI hat ein sehr gutes Handbuch mit Beispielcode und detaillierten Erklärungen zu jeder Methode. Ich konnte an einem Nachmittag 20 C-Funktionen umbrechen und musste den Code seitdem nicht mehr berühren.
Der andere Faktor war, dass ich mit Common Lisp besser vertraut bin als mit Clojure oder dem R6RS-Schema. Ich habe die meisten Bücher von Practical Common Lisp und Graham gelesen und bin mit dem Hyperspec vertraut. Es ist noch kein "lispy" Code, aber ich bin mir sicher, dass sich das ändern wird, wenn ich mehr Erfahrung sammle.
quelle
Ich programmiere sowohl in CL als auch in Racket.
Ich entwickle gerade eine Website in Common Lisp und habe eine Reihe von internen Programmen für meinen früheren Arbeitgeber in Racket geschrieben.
Für den internen Code entschied ich mich für Racket (damals als PLT-Schema bekannt), da der Arbeitgeber ein Windows-Shop war und ich sie nicht dazu bringen konnte, für LispWorks zu bezahlen. Die einzige gute Open-Source-CL-Implementierung für Windows war (und ist) CCL, für die SSE-Unterstützung im Prozessor erforderlich ist. Der Arbeitgeber, der billig war, verwendete steinzeitliche Hardware. Selbst wenn der Arbeitgeber über angemessene Hardware verfügte, ist McCLIM die einzige GUI-Bibliothek mit Konsequenz in Common Lisp, die nur unter Unix funktioniert. Racket hat eine gute GUI-Bibliothek, die sowohl unter Unix als auch unter Windows funktioniert, was für den Erfolg meines Projekts entscheidend war.
Ich habe über ein Jahr damit verbracht, mich mit dem primitiven DrRacket-Editor auseinanderzusetzen. EMACS konnte die GUI-Version von Racket, damals als MrEd bekannt, unter Windows nicht in ein minderwertiges Lisp verwandeln. Ich musste darauf verzichten, den Ausdruck am Cursor mit einem einzigen Tastendruck auswerten zu können. Stattdessen musste ich den S-Ausdruck manuell auswählen, kopieren, auf das REPL-Fenster klicken (da kein Tastendruck erforderlich ist) und dann den S-Ausdruck einfügen. Ich musste auch auf einen Editor verzichten, der mir die erwarteten Argumente der von mir verwendeten Funktion oder des verwendeten Makros zeigen konnte. DrRacket ist kein Ersatz für SLIME.
Der Arbeitgeber verwendete eine proprietäre Datenbank mit einer komplizierten XML-API, die eine Menge scheinbar unnötiger Informationen erforderte, um auf die Version einer SELECT-Abfrage antworten zu können. Ich habe beschlossen, HTMLPrag zu verwenden, um XML an diese API zu senden und die Antworten zu analysieren. Es hat super geklappt.
Ich musste das überkomplizierte "Syntaxfall" -Makrosystem von Racket erlernen, um ein Makro zu schreiben, mit dem ich mit der überkomplizierten XML-API interagieren und Formulare eingeben konnte, die wie SQL aussahen. Dieser Teil wäre viel einfacher gewesen, wenn ich DEFMACRO zur Verfügung hätte. Das Endergebnis war jedoch immer noch nahtlos, obwohl mehr Anstrengungen erforderlich waren, um es zu erreichen.
Außerdem musste ich auf das LOOP-Makro von Common Lisp verzichten. Racket bot eine Alternative erst an, nachdem ich den größten Teil des Codes geschrieben hatte, und die Alternative ist im Vergleich zu LOOP immer noch mies (obwohl das Entwicklerteam von Racket darauf besteht, dass es besser ist - sie sind einfach falsch). Am Ende habe ich viele benannte LET-Formulare geschrieben, in denen "car" und "cdr" verwendet wurden, um Listen zu durchlaufen.
Apropos Auto und CDR, nichts ist frustrierender als Schemas Interpretation von (auto '()) als Fehler. Ich habe die Groß- und Kleinschreibung von Racket ausgenutzt und CAR und CDR implementiert, die die Semantik von Common Lisp haben. Die Trennung von '() und #f macht es jedoch weitaus weniger sinnvoll,' () als Standardwert zurückzugeben.
Schließlich habe ich UNWIND-PROTECT neu implementiert und mein eigenes Neustart-System erfunden, um die Lücke zu füllen, die Racket hinterlassen hat. Die Racket-Community muss lernen, dass Neustarts sehr nützlich und einfach zu implementieren sind.
Die Let-Values-Form von Racket war zu ausführlich, sodass ich MULTIPLE-VALUE-BIND implementiert habe. Das war absolut notwendig, weil Racket erfordert Sie erhalten alle die Werte , die erzeugt werden, ob Sie sie verwenden oder nicht.
Später habe ich versucht, einen eBay XML API-Client in Common Lisp zu schreiben. Dabei stellte ich fest, dass er nicht mit HTMLPrag vergleichbar ist. HTMLPrag ist verdammt nützlich. Am Ende habe ich dieses Projekt in Racket gemacht. Ich experimentierte mit den Literate Programming-Funktionen von Racket, um herauszufinden, dass ich der einzige Programmierer auf der Erde bin, der es schwieriger findet, richtig geschriebenen Text zu bearbeiten als gewöhnlichen Code oder falsch geschriebenen, übermäßig kommentierten Text.
Mein neues Projekt wird in Common Lisp durchgeführt, was die richtige Wahl war, da die Racket-Community einfach nicht an Parallelität glaubt, was für dieses Projekt von wesentlicher Bedeutung ist. Das einzige, von dem ich dachte, dass ich es von Racket verpasst hätte, waren Fortsetzungen. Ich war jedoch in der Lage, mithilfe von Neustarts das zu tun, was ich brauchte, und im Nachhinein hätte ich es wahrscheinlich mit einem einfachen Abschluss tun können.
quelle
cond
Formulars einfügen) und Fehler (habe ich den Loop-Terminierungstest damals richtig geschrieben?) Hinzuzufügen. Selbst heute habe ich den Eindruck gewonnen Dieser Schläger ist hauptsächlich für Studenten und nicht für professionelle Programmierer gedacht. Jedes Mal, wenn ich höre, dass jemand außer mir es benutzt, spricht er die Subsprache "Beginning Student" und es ist für eine Klasse.Schema ist mit einer separaten Zusammenstellung konzipiert. Infolgedessen ist die Leistung der Makros häufig stark eingeschränkt, selbst bei Erweiterungen, die ein Defmacro im Common Lisp-Stil anstelle eines schlechten, einschränkenden hygienischen Makrosystems ermöglichen. Es ist nicht immer möglich, ein Makro zu definieren, das ein anderes Makro definiert, das für die sofortige Verwendung in einer nächsten Codezeile vorgesehen ist. Eine solche Möglichkeit ist für die Implementierung effizienter eDSL-Compiler unabdingbar.
Es erübrigt sich zu erwähnen, dass Schema-Implementierungen mit nur R5RS-Hygienemakros für mich kaum nützlich sind, da mein Metaprogrammierungsstil nicht angemessen auf Hygiene übertragen werden kann.
Glücklicherweise gibt es Schema-Implementierungen (z. B. Racket), für die diese Einschränkung nicht gilt.
quelle