T.M. schriebSorry, aber was soll das sein? Eine Funktion, die eine Datei liest und meinetwegen zeilenweise ausgibt?
Nein, ich wollte keine Funktion, ich wollte ein syntasches Konstrukt, ein Statement, wenn Du so willst.
ifstream i("Dateiname", ios::in);
string line;
while (getline(i, line))
{
       cout << line << endl;
}
Was Du hier in Ansätzen gemacht hast ist, die Arbeit, welche Dein Statement übernehmen sollte, von Hand zu verrichten. Um beim Bauern zu bleiben: Er zieht gerade seinen Pflug höchstpersönlich, der Ochse macht diese Arbeit nicht, weil es das Komitee der ANSI-Ochsen nicht vorsah, und hat einen Stein draufgelegt, damit er wenigstens ein wenig Arbeit erledigt bekommt.

Um bei dem Snippet zu bleiben:
ifstream i("Dateiname", ios::in);
string line;
try {
------------------------------
    while (getline(i, line))
    {
        cout << line << endl;
    }
------------------------------
} finally {   // gibts finally in c++?
    close(i); // oder wie immer es heißen mag
}
Alles ober- und unterhalb beziehungsweise (zuzüglich des Schliessen des FD, selbstverständlich abgesichert gegen Sprünge, bzw. Exceptions) unterhalb der Linien ist nicht Deine Aufgabe, sondern Aufgabe Deines syntaktischen Konstruktes. Wird in C++-Kreisen wirklich immer noch dieser ganze Boilerplate geschrieben, wenn man Code unter einem geöffneten Filedescriptor laufen lassen will? Das wäre mir zu blöd. Wie oft machst Du das in Deinem Leben? Hundertmal im Jahr? Wenn Du es ein paar Tausend mal machst, wie oft wirst Du eine Zeile vergessen und FDs leaken?
[Was der Messias uns an Libs beschert wird alles richten]
Nein, wird es nicht.

Erstens wird es in standardgemäßem C++ keiner Lib der Welt möglich sein, DIR diese Abstraktionsmöglichkeit zu bieten, ohne erheblichen Aufwand (auch Deinerseits) betreiben zu müssen, den Du nicht auf Dich nehmen wollen wirst. In der Konsequenz bleibt die (hypothetische) Möglichkeit ob ihrer Komplexität ungenutzt. Zweitens wollten wir ja nicht auf den Erlöser warten, sondern unsere Sprache benutzen um Arbeit zu erledigen. Hier und jetzt.

Es geht auch in keinster Weise um irgendwelche obskure Schnittstellen, es geht allein um Abstraktionen, beziehungsweise deren Fehlen.

Und selbst wenn es einem genialen C++ Hacker (denn um das in dieser Sprache zu machen, muss man wohl einer sein) es gelingt und er Dir ein gewisses Maß dieser Flexibilität ermöglicht, kannst Du damit ein with_initialized_gui(), oder without_interrupts() schreiben, welches Dir diese langweilige und fehleranfällige Arbeit abnimmt? Das ist die Frage: Kannst Du es machen, oder muss es der Designer, das Komitee für dich machen? Falls Du es nicht kannst: Weiß der Sprachdesigner, was du brauchst und in unbestimmter Zukunft brauchen wirst? Wahrscheinlich wirst Du einfach darauf verzichten und den Boilerplate weiterhin von Hand schreiben, wie Generationen von C++-Hackern vor Dir.

Wie siehts mit der zweiten Aufgabe aus?
@danlei, T.M.:

Ihr sprengt jetzt aber den Thread: 😉 Soo genau wollten wir das hier doch gar nicht wissen. (Obwohl ich eure Diskussion ausgesprochen spannend finde, aber das wäre eigentlich Stoff für einen eigenen Thread.)

Zu C++ selbst gibt es für mich drei Feststellungen.
  • C++ hat als eine Sammlung von C-Makros angefangen und wird dieses Krücken-Erbe nie wieder los.
  • Man kann in C++ sauber programmieren, aber es verlangt ausgesprochen viel Disziplin. Die meisten C++-Programme sind einfach deswegen Mist, weil sie konzeptionslos zusammengeschustert worden sind: Pseudo-Objekte überall und gewaltiges Schindluder mit Vererbungsstrategien getrieben.
  • Der eigentliche "Reiz" von C++ besteht für mich darin, dass ich eigentlich prozedural, quasi auf C-Ebene, arbeiten kann und bei Bedarf Objekte zur Verfügung habe. Nur kann ich dann auch wieder mit Python arbeiten.
Ich bin seinerzeit über Smalltalk und Eiffel zur Objektprogrammierung gekommen. Smalltalk liebe ich noch immer heiß und innig (aber kann es kaum noch, wie ich eben an euren Beispielen merke 🙁 ). Und ich bin immer noch der Überzeugung, dass man das Objekt- und Vererbungskonzept eigentlich nur verstehen kann, wenn man mal Eiffel verinnerlicht hat.

Im Lauf der Zeit bin ich aber zu dem Schluss gekommen, dass zu starke Konzentration auf objektorientierte Mittel viele Wege zur Problemlösung versperrt. So bin ich bei allen Ausflügen in unterschiedliche Programmierkonzepte und -Strategien doch immer wieder bei Lisp gelandet.

Es muss ja nicht unbedingt Emacs sein. Obwohl sonst kaum irgendwo noch Lisp in der "breiten Masse" auftaucht. Erinnert sich überhaupt noch jemand daran, dass es mal Lisp-Maschinen, d.h. Lisp-orientierte Hardware gab?
bernarcher schrieb Ihr sprengt jetzt aber den Thread: 😉 Soo genau wollten wir das hier doch gar nicht wissen. (Obwohl ich eure Diskussion ausgesprochen spannend finde, aber das wäre eigentlich Stoff für einen eigenen Thread.)
Hmpf, jetzt werd ich auch noch in meinem Editier-Rausch unterbrochen. 🙂
Im Lauf der Zeit bin ich aber zu dem Schluss gekommen, dass zu starke Konzentration auf objektorientierte Mittel viele Wege zur Problemlösung versperrt. So bin ich bei allen Ausflügen in unterschiedliche Programmierkonzepte und -Strategien doch immer wieder bei Lisp gelandet.
Nicht nur bei OOP, sondern immer, wenn es nicht um die Problemlösung, sondern um die Erfüllung bestimmter "Rituale" (Boilerplate z.B., Edit-Compile-Run-Debug-Go-Insane-Cyle) geht, läuft etwas falsch, mMn.
Es muss ja nicht unbedingt Emacs sein. Obwohl sonst kaum irgendwo noch Lisp in der "breiten Masse" auftaucht. Erinnert sich überhaupt noch jemand daran, dass es mal Lisp-Maschinen, d.h. Lisp-orientierte Hardware gab?
So ein Ding im Keller ... das wär was ...
danlei schriebich wollte ein syntasches Konstrukt, ein Statement, wenn Du so willst.
Der Sinn eines solchen will sich mir beim besten Willen nicht erschliessen. Ich schreibe nicht jeden Tag eine solche Schleife zum Lesen einer Datei, nicht einmal jede Woche, nicht einmal jeden Monat, obwohl ich 8 Stunden jeden Tag C++ mache. Wozu soll die Sprache da ein Statement anbieten? Das ist irgendwie albern, ehrlich.
danlei schriebUm bei dem Snippet zu bleiben:
T.M. schrieb
ifstream i("Dateiname", ios::in);
string line;
------------------------------
while (getline(i, line))
{
       cout << line << endl;
}
------------------------------
close(i); // oder wie immer es heißen mag
Alles ober- und unterhalb beziehungsweise (zuzüglich des Schliessen des FD, selbstverständlich abgesichert gegen Sprünge, bzw. Exceptions) unterhalb der Linien ist nicht Deine Aufgabe, sondern Aufgabe deines syntaktischen Konstruktes.
Also erstens wird das Schliessen in meinem Fall vom Destruktor erledigt. Und der implizite Aufruf eines Destruktors *ist* ein syntaktischen Konstrukt, übrigens bemerkenswerter Einfachheit. Ich sagte ja, dieses Schnipsel sei dicht. Zwotens ging es um einen geöffneten Filedeskriptor, ich nehme also an, dass der dann auch offenbleiben soll. Die Destruktoren der generischen stream-Klassen machen das in diesem Fall auch so. Drittens, wenn Du Code haben willst, der eine vernünftige Fehlerbehandlung durchführt (exceptions), dann will ich gerne mal sehen, wie das Dein eines Statement macht.

Einschub: die exception-Behandlung in C++ ist tatsächlich verbesserungsbedürftig. Es mangelt insbesondere an einem Konstrukt, vor bzw. hinter die Anweisung, die die exception ausgelöst hat, zu springen, um die Anweisung (ggf. nach Änderung äusserer Bedingungen) neu auszuführen oder zu überspringen. Aber welche Sprache kann das?
danlei schriebWird in C++-Kreisen wirklich immer noch dieser ganze Boilerplate geschrieben, wenn man Code unter einem geöffneten Filedescriptor laufen lassen will? Das wäre mir zu blöd. Wie oft machst Du das in Deinem Leben?
Also ehrlich, von einem geöffneten Filedeskriptor zu lesen, das ist mir in den letzten 20 Jahren genau zweimal begegnet. Ich kann mich sogar an beide Fälle erinnern! Genügt das, um es von Hand schreiben zu dürfen?
danlei schriebErstens wird es in standardgemäßem C++ keiner Lib der Welt möglich sein, DIR diese Abstraktionsmöglichkeit zu bieten, ohne erheblichen Aufwand (auch Deinerseits) betreiben zu müssen, den Du nicht auf Dich nehmen wollen wirst.
Der Aufwand besteht genau in obigem Schnipsel mit dem einen Unterschied, dass eine andere stream-Klasse verwendet werden muss, wenn man einen geöffneten Filedeskriptor verwenden will.
danlei schriebWie siehts mit der zweiten Aufgabe aus?
Also erstens hab ich nebenbei auch noch irgendwie zu arbeiten. Zwotens bist Du noch schuldig, eine Sprache zu nennen, die das von Dir geforderte hoffentlich einzeilige Statement anbietet. Drittens scheint mir letzteres Problem eines zu sein, das durch Mehrfachvererbung halbwegs zu lösen sein sollte.

Viertens allerdings nimmt die Diskussion nun einen Verlauf, der irgendwie weit abseits führt. Der Sinn ist mir nicht klar. Du magst C++ nicht. Das hast Du zum Ausdruck gebracht. Ich sage nicht, es sei der Weisheit letzter Schluss, und ich behaupte auch nicht, es sei wunder wie schön, aber ich sage, es ist weit entfernt von der durch Dich mit einiger Polemik postulierten Unbrauchbarkeit. Ich würd's dabei belassen.
bernarcher schrieb@danlei, T.M.: * Man kann in C++ sauber programmieren, aber es verlangt ausgesprochen viel Disziplin.
Weniger als man denkt. Ich würde es allerdings begrüssen, wenn der Compiler durch deutliche Warnungen schlechten Stil anmeckern würde. Es gibt übrigens Compiler, die das machen.
bernarcher schriebDie meisten C++-Programme sind einfach deswegen Mist, weil sie konzeptionslos zusammengeschustert worden sind: Pseudo-Objekte überall und gewaltiges Schindluder mit Vererbungsstrategien getrieben.
Dem schliesse ich mich uneingeschränkt an. Insbesondere über lange Jahre der Entwicklung mit unterschiedlichen Personen (und sogar Firmen) an demselben Code.
bernarcher schriebIch bin seinerzeit über Smalltalk und Eiffel zur Objektprogrammierung gekommen. Smalltalk liebe ich noch immer heiß und innig
Smalltalk ist genial. Aber es ist ewig her ... Ich hab mindestens zehn Jahre keins mehr gesehen. Ich kenn auch niemanden, der Smalltalk professionell einsetzt.
bernarcher schriebIm Lauf der Zeit bin ich aber zu dem Schluss gekommen, dass zu starke Konzentration auf objektorientierte Mittel viele Wege zur Problemlösung versperrt.
Auch dieser Satz ist richtig. OOP zerfranst insbesondere Algorithmen bis zur völligen Unlesbarkeit. Man kann in grossen Klassenhierarchien mit exzessivem Überschreiben eben nicht mehr konkret vorhersagen und sich drauf verlassen, welches Objekt sich wie verhält. Die Folge sind stunden- und tagelange Debuggingsitzungen, woraufhin man am Ende eine einzelne Zeile ändert ...
T.M. schriebDer Sinn eines solchen will sich mir beim besten Willen nicht erschliessen. Ich schreibe nicht jeden Tag eine solche Schleife zum Lesen einer Datei, nicht einmal jede Woche, nicht einmal jeden Monat, obwohl ich 8 Stunden jeden Tag C++ mache. Wozu soll die Sprache da ein Statement anbieten? Das ist irgendwie albern, ehrlich.
Damit habe ich fast gerechnet. Also, zunächst geht es nicht allein um das bereitstellen eines FD, das war lediglich ein sehr einfaches Beispiel um Dir eine Lösung wenigstens theoretisch irgendwie möglich zu machen.

Hast Du auch beim hello-world gesagt: "Warum um alles in der Welt sollte ich 'Hello' auf einem Bildschirm ausgeben wollen?" Natürlich ging es nicht um das Hallo.

Die Schleife ist auch in diesem Beispiel gar nicht gegenstand der Aufgabe, im Body dieses Statements könnte /jeglicher/ Code stehen. Die Schleife mit zu abstrahieren wäre wahrscheinlich Blödsinn. (Wobei man ja nie weiss, ob mans mal braucht. Brauchte ich es, wäre es kein Problem, es in ein paar Zeilen zu implementieren.)

Gut, ich glaube Dir, dass Du sehr selten den Beispielcode brauchst, aber wenn Du mir allen Ernstes sagen willst, dass es in deiner Arbeit keinen Boilerplate gibt, dann kann ich das nicht glauben. Das von mir angeführte Beispiel steht für /alle/ wiederholt anfallenden Codemuster, nicht nur für dieses kleine Beispiel.

In Sprachen, die das ermöglichen /gibt/ es keinen Boilerplate, den man nicht freiwillig toleriert (was meist nicht passiert).

Und wieder genau die Einstellung, gegen Die ich argumentiere: Ich will eben /nicht/, dass C++ dieses Statement bekommt. Genau das ist der Knackpunkt. Der Sinn der Sache ist, dass Du Dir solch ein Statement bequem beim ersten erkennen sich wiederholender Muster in Deinem Code /selbst/ erstellen kannst.

Zu Deinen acht Stunden Arbeit: Ich glaube Dir, dass Du ein Fähiger Programmiere bist, wohl auch über C++ hinaus, aber beantworte mir eine Frage: Wenn Dir diese Möglichkeiten auch nur eine Stunde am Tage ersparten (was vorsichtig geschätzt ist, wenn Du an diese Abstraktionsmöglichkeiten gewöhnt bist, fallen Dir täglich Dinge auf, die eigentlich nur "Ritual" sind), hätte es sich dann nicht schon dicke gelohnt? Meine Antwort wäre "ja".

Um es anders auszudrücken: Du benutz einen Compiler, einen Übersetzer, musst aber in Deiner Sprache sehr viel Arbeit, für die dieser Übersetzer eigentlich da sein sollte, zumindest könnte, selbst übernehmen, weil Du keine (benutzbare) Möglichkeit hast, ihn auf deine Anforderungen hin zu optimieren.
Also erstens wird das Schliessen in meinem Fall vom Destruktor erledigt. Und der implizite Aufruf eines Destruktors *ist* ein syntaktischen Konstrukt, übrigens bemerkenswerter Einfachheit. Ich sagte ja, dieses Schnipsel sei dicht. Zwotens ging es um einen geöffneten Filedeskriptor, ich nehme also an, dass der dann auch offenbleiben soll. Die Destruktoren der generischen stream-Klassen machen das in diesem Fall auch so. Drittens, wenn Du Code haben willst, der eine vernünftige Fehlerbehandlung durchführt (exceptions), dann will ich gerne mal sehen, wie das Dein eines Statement macht.
Wie oben angeführt: Ok, in diesem Falle musst Du nicht hinterher aufräumen, also bleibt es bei deinem ersten Snippet, aber auch ohne den try/finally block ist und bleibt es Boilerplate. Ich schätze Dich als schlau genug ein, dass Du besseres mit dieser Zeit anfangen könntest.

Was nun, wenn das zu abstrahierende Muster nicht vier Zeilen umfasst, sondern zehn? Wo ist die Grenze, ab der Du zugestehst, dass man solchen Kram nicht von Hand schreiben sollte?

(Muster wie gesagt im textuellen sinne, nicht im Sinne von "Entwurfsmuster".)
Einschub: die exception-Behandlung in C++ ist tatsächlich verbesserungsbedürftig. Es mangelt insbesondere an einem Konstrukt, vor bzw. hinter die Anweisung, die die exception ausgelöst hat, zu springen, um die Anweisung (ggf. nach Änderung äusserer Bedingungen) neu auszuführen oder zu überspringen. Aber welche Sprache kann das?
Common Lisp. Selbst, wenn wir nicht das beste Condition-System hätten, dass mir je unter die Augen gekommen ist, könnte ich es mir (relativ) mühelos erstellen; mit eben den Mitteln, deren Sinn sich Dir (noch) verschließt.
Der Aufwand besteht genau in obigem Schnipsel mit dem einen Unterschied, dass eine andere stream-Klasse verwendet werden muss, wenn man einen geöffneten Filedeskriptor verwenden will.
Nochmal: Es geht nicht um irgendwelche Stream-Klassen, sondern darum, dass Du Sachen von Hand schreiben musst, sich wiederholende Muster, nicht abstrahieren kannst. Eine if Anweisung wird in Sprünge übersetzt; würde es Dir etwas ausmachen, diese Sprünge heutzutage noch von Hand zu schreiben? Das Beispiel ist Dir zu grobschlächtig? Ok. Wie ist es mit for-Schleifen, die über Datenstrukturen iterieren? Ist es nicht nett foreach blabla schreiben zu können (wie auch immer es in modernem C++ heißt), ohne sich um diesen Kram kümmern zu müssen? Nun geht es aber nicht um /ein/ spezifisches Konstrukt, sondern darum, dass Du Dir, nach Deinen Anforderungen die Abstraktionen schaffen kannst, die Dir hilfreich und sinnvoll erscheinen.

Mit ähnlichen Argumenten (Darf ich diese Paar Zeilen jetzt nicht mehr von Hand schreiben? Das ist doch schnell gemacht. Ich sehe den Sinn nicht ein) haben sich damals die alten Assembler-Hacker mit Händen und Füßen gegen Hochsprachen gewehrt.
danlei schriebWie siehts mit der zweiten Aufgabe aus?
Also erstens hab ich nebenbei auch noch irgendwie zu arbeiten. Zwotens bist Du noch schuldig, eine Sprache zu nennen, die das von Dir geforderte hoffentlich einzeilige Statement anbietet. Drittens scheint mir letzteres Problem eines zu sein, das durch Mehrfachvererbung halbwegs zu lösen sein sollte.
Meine Schuld hänge ich gleich an, keine Sorge.

Also, zur zweiten Aufgabe: Ich denke, dass sie in C++ nur sehr umständlich zu Lösen sein wird. Schuldig bist Du mir die Lösung nicht, vor allem, da du wahrscheinlich auch bei diesem Beispiel, wie auch in diesem Falle schon von mir vorausgesagt, seinen Wert nicht erkennen wirst. Warum? Allah.

Nur weil Du etwas in Deinem Arbeitsaltag nicht benutzt (wie auch, wenn Du die Möglichkeit nicht einmal kennst, geschweige den Sinn derselben) heißt das nicht, dass Dir nicht etwas jenseits der C++-Welt entgeht. Du kannst ob meines forschen Tones beleidigt sein, es vollkommen ignorieren, oder es Dir wenigstens im Hinterkopf behalten für Zeiten, in denen du deine Zeit nicht mehr mit Arbeit verschwenden willst, die der Compiler für Dich erledigen sollte.
Viertens allerdings nimmt die Diskussion nun einen Verlauf, der irgendwie weit abseits führt. Der Sinn ist mir nicht klar. Du magst C++ nicht. Das hast Du zum Ausdruck gebracht. Ich sage nicht, es sei der Weisheit letzter Schluss, und ich behaupte auch nicht, es sei wunder wie schön, aber ich sage, es ist weit entfernt von der durch Dich mit einiger Polemik postulierten Unbrauchbarkeit. Ich würd's dabei belassen.
Sinn? Sagen wir Missionierung, nur ohne das Ausbeuten. 🙂 Spaß beiseite: Dieser Artikel sollte sich freundlicher lesen, ich habs zumindest versucht.

Aber nun zu meiner Schuld:
(with-open-file (file "/foo/bar/baz")
  ...
  ...
  ...)
Eine einfache Implementierung:
(defmacro my-with-open-file ((var filename) &body body)
  `(let ((,var (open ,filename)))
     (unwind-protect
       ,@body
       (close ,var))))
Und das war auch schon die ganze Geschichte, das letzte Mal, das ich Boilerplate zum Öffnen einer Datei und allem Drumherum geschrieben habe. (Hätte, wenn es nicht sowieso schon im Standard wäre.)

Nochmal zum Sinn der Sache:

Das ganze ist auf jegliche Art von Code anwendbar. Ein Beispiele für diese Art Abstraktion wären z.B. with-html-output, with-input-from-string. Kurz, alles, was einen Kontext etabliert. In nicht wenigen Fällen kann diese Art der Abstraktion (und diese schließt /nicht/ andere Abstraktionsarten wie OOP aus) sparen Dir Diese Dinge wirklich eine Heidenarbeit.

Damit ist die Sache aber noch nicht am Ende: Auch Kontrollstrukturen und ähnliches lassen sich so Implementieren. Weiter oben erwähntest Du, dass Du Verbesserungsbedarf im Exception-Handling begrüßen würdest. Nun, wenn mir mein Condition-System nicht gefiele, so würde ich es mit eben diesen Mitteln zu dem machen, was ich brauche.

Also, ich habe mir redliche Mühe gegeben, es zu erklären und bin für Fragen offen. Vielleicht habe ich ja erreicht, dass Du wenigstens in Erwägung ziehst, dass ich Dir keinen Unsinn verzapfe, sonder Du mir glaubst, dass ich von etwas rede, was ich als in einer Sprache wichtig erachte. Meine Hoffnung ist allerdings relativ gering, denn ich kann mich an Zeiten erinnern, als ich mit BASIC voll und ganz zufrieden war. (Eben bevor ich andere Sachen kennengelernt und ihren Sinn verstanden habe.)

Soviel zur syntaktischen Abstraktion. Die OOP-Aufgabe wäre auch noch interessant, aber das kannst Du halten, wie Du willst.
Wo ihr doch gerade so schön übereinander herzieht(^^), könntet ihr doch gleich auch noch eine Meinung zu Haskell äußern, die nicht auf "Das benutzt eh keiner" o.ä. beruht…
linopolus schriebWo ihr doch gerade so schön übereinander herzieht(^^), könntet ihr doch gleich auch noch eine Meinung zu Haskell äußern, die nicht auf "Das benutzt eh keiner" o.ä. beruht…
Warum nicht einfach ausprobieren? Tutoriale sind im Web, ghc auch und wo Haskell einzuordnen ist (streng funktional, bedarfsauswertend, statisch getypt, aber typableitend) hast ja hier eh mitbekommen. Oder gibts noch konkrete Fragen?
danlei schrieb
(with-open-file (file "/foo/bar/baz")
  ...
  ...
  ...)
Eine einfache Implementierung:
(defmacro my-with-open-file ((var filename) &body body)
  `(let ((,var (open ,filename)))
     (unwind-protect
       ,@body
       (close ,var))))
Ich will meine Hand dafür jetzt nicht ins Feuer legen, aber ich glaube sowas sollte auch mit C-Makros möglich sein.

Zumal ich konkret in dem Beispiel jetzt keinen großen Gewinn im Vergleich zur OO von C++ sehe. Eine Initialisierung am Anfang und ein implizierter Desktruktor-Aufruf machen das gleiche; statt "file" muss man eben immer das Objekt angeben, was jetzt nicht weiter wichtig ist.

Egal, ich finde diese Meta-Programmierung schon sehr interessant, und hätte dazu mal eine Frage, wenn das ok ist:

Ist es auch möglich Funktionen kontextabhängig zu überladen (ich weiß nicht, ob das die richtige Terminologie ist)?

Konkret schwebt mir da etwa Folgendes vor: Ich habe ne Datenbank-Anwendung und möchte so gut es geht, von der Datenbank abstrahieren. Wäre dann so etwas möglich:
(sqlite3 ("/pfad/zur/datenbank")
  …
  (query "select * from some_table")
  …
)
Also zunächst soll eine Datenbank-Verbindung aufgebaut werden und anschließend beziehen sich alle SQL-Queries auf diese Verbindung. Ob ich dabei ne Sqlite oder Mysql oder sonstiges benutze, sollte dementsprechend keine Rolle spielen, weil je nachdem die "query"-Funktion anders arbeitet.

Wäre das, oder sowas ähnliches, in Common Lisp möglich?

Gruß
Kinch
danlei schriebGut, ich glaube Dir, dass Du sehr selten den Beispielcode brauchst, aber wenn Du mir allen Ernstes sagen willst, dass es in deiner Arbeit keinen Boilerplate gibt, dann kann ich das nicht glauben. Das von mir angeführte Beispiel steht für /alle/ wiederholt anfallenden Codemuster, nicht nur für dieses kleine Beispiel.

In Sprachen, die das ermöglichen /gibt/ es keinen Boilerplate, den man nicht freiwillig toleriert (was meist nicht passiert).
(Nur als Hinweis, vielleicht wäre mir eher klargeworden, was Du eigentlich meinst, wenn Du Dich präziser ausdrücken würdest. Das Buzzword "Boilerplate", das wiederholt gefallen ist, sagt mir schlicht überhaupt nichts.)
danlei schriebIch will eben /nicht/, dass C++ dieses Statement bekommt. Genau das ist der Knackpunkt. Der Sinn der Sache ist, dass Du Dir solch ein Statement bequem beim ersten erkennen sich wiederholender Muster in Deinem Code /selbst/ erstellen kannst.
Das ist präzise! Das versteh ich. Aber hey, C++ ist reich an derartigen Möglichkeiten, auf ganz verschiedenem Niveau. Es geht über den alten, klassischen Präprozessor, über Funktionstemplates und überladbare Operatoren (allein diese beiden Konzepte sind genial vielseitig), neuerdings bis hin zu Lambdafunktionen. Ich erwähnte kürzlich hier den Spirit Parser. Dieser Teil der boost erlaubt das Schreiben eines Parsers und nutzt in beeindruckender Weise gerade diese Möglichkeiten von C++. Man merkt fast nicht mehr, daß es nichts weiter als echtes und reines C++ ist, man schreibt genaugenommen BNF, eine Sprache in der Sprache, und kann sich voll und ganz darauf konzentrieren. Und sicher, solche Art Statements zu definieren, wie Du meinst, ist eine lohnende Aufgabe. Aber das macht doch jeder mit den Mitteln, die er hat. Die <algorithm>-Bibliothek der STL ist beispielsweise genau so etwas. Die boost definiert noch zahlreiche weitere Sachen, z.B. foreach, um Container zu durchlaufen, ohne die lästigen Iteratoren explizit anlegen zu müssen.
danlei schriebZu Deinen acht Stunden Arbeit: Ich glaube Dir, dass Du ein Fähiger Programmiere bist, wohl auch über C++ hinaus, aber beantworte mir eine Frage: Wenn Dir diese Möglichkeiten auch nur eine Stunde am Tage ersparten (was vorsichtig geschätzt ist, wenn Du an diese Abstraktionsmöglichkeiten gewöhnt bist, fallen Dir täglich Dinge auf, die eigentlich nur "Ritual" sind), hätte es sich dann nicht schon dicke gelohnt? Meine Antwort wäre "ja".
Selbstverständlich. Nur, meine Arbeit besteht zum aller geringsten Teil aus solcher Routine. Ich muß mich ehrlich gesagt auch wundern, daß beispielsweise Entwicklungsumgebungen heute mit Wizards, Codetemplates und Codegeneratoren ankommen und dies als wesentliche features verkaufen. Das brauch ich seltenst, einmal im Jahr (und dann mißlingt es noch ...) Die wirklich kostbare Zeit geht bei einem Projekt in der Größenordnung von tausend selbstgeschriebenen Klassen bei ganz anderen Dingen verloren, nämlich bei tiefen inhaltlichen Problemen, und das ist in den meisten Sprachen so. Ich fühle mich dann eigentlich selten explizit durch C++ behindert, so daß ich mir wünschte, ich könnte das jetzt anders schreiben, eleganter, schneller. Wenn ich Routine feststelle, dann löse ich das durch Refaktorierung, also indem ich mir eine Funktion mache, die das richtige tut, im einfachsten Fall ein Makro (und es gibt ganz bemerkenswerte Makros, vor allem in älterem Code, als es noch keine Funktionstemplates gab, z.B. in der MFC). Compilierzeiten sind ferner ein C++-spezifisches Thema, da geht Zeit verloren. Wenn man einen globalen Header ändert, kann das durchaus zwei, drei Stunden (oder einen Tag ...) Pause bedeuten. Aber der C++-Programmierer weiß die Zeit vielleicht anderweitig auch zu nutzen.
danlei schriebWas nun, wenn das zu abstrahierende Muster nicht vier Zeilen umfasst, sondern zehn? Wo ist die Grenze, ab der Du zugestehst, dass man solchen Kram nicht von Hand schreiben sollte?
Der Ansatz von STL und boost, ich sag's immer wieder. C++ lernen sollte eigentlich heißen: die STL verstehen lernen.
danlei schriebAlso, zur zweiten Aufgabe: Ich denke, dass sie in C++ nur sehr umständlich zu Lösen sein wird. Schuldig bist Du mir die Lösung nicht, vor allem, da du wahrscheinlich auch bei diesem Beispiel, wie auch in diesem Falle schon von mir vorausgesagt, seinen Wert nicht erkennen wirst. Warum? Allah.
Letzteres ist nichts als eine Mutmaßung. - Der Wert ist mir schon klar, nur ist es auch ein handfestes Problem. Es läuft auf Polymorphie in zwei orthogonalen Richtungen hinaus, wie gesagt: zwei unabhängige Klassenhierarchien und dann Zusammenführung durch Mehrfachvererbung, letzteres vielleicht in einem template, dann könnte man vielleicht dort eine generische Druckmethode machen. Ich würde dies allerdings, wenn's geht, vermeiden, zur Not sogar den OO-Ansatz verlassen. Die Frage ist, ob dies dann umständlich wäre.
danlei schriebWeiter oben erwähntest Du, dass Du Verbesserungsbedarf im Exception-Handling begrüßen würdest. Nun, wenn mir mein Condition-System nicht gefiele, so würde ich es mit eben diesen Mitteln zu dem machen, was ich brauche.
Ha, an das exception handling ist in C++ nicht heranzukommen. Ich sag ja, dies ist tatsächlich eine Schwäche. Und jedes technische System hat irgendwo seine Grenzen. Exception handling erfordert komplizierte interne Strukruren, wer mal einen Compiler geschrieben hat, weiß, daß diese gelegentlich im Umfang sogar den eigentlichen Programmcode übersteigen können, nur um in irgendeinem seltenen Notfall etwas sinnvolles zu tun ... Drum laufen Programme mit eingeschaltetem exception handling auch langsamer als ohne. Es stört aber kaum. Viel schlimmer ist in der Praxis, daß der eine exceptions verwendet, der andere return codes, der dritte beides (so idiotisch das auch ist), der vierte nimmt globale Statusvariablen und der fünfte gar nichts, und zwar das alles noch je nach Tagesform - *das!* ist ein Problem.

Was das Missionieren angeht, danke für die Mühe. Es ist ja nicht so, daß ich nicht auch links und rechts schauen würde. So bin ich ja auch zu C++ gekommen. Ich hab eigentlich mit Assembler angefangen, für zwei grundverschiedene Prozessoren, und erst, als ich an eine Produktivitätsgrenze stieß, wurde mir klar, es muß effektiver gehen. Aber eben diese Grenze ist bei mir hinsichtlich C++ noch längst nicht erreicht. C++ ist quasi mit mir mitgewachsen, in genau derselben Geschwindigkeit. Das Schauen links und rechts bleibt bislang Spiel, so interessant es sein mag.
Kinch schriebIch will meine Hand dafür jetzt nicht ins Feuer legen, aber ich glaube sowas sollte auch mit C-Makros möglich sein.
Möglich ist, wie wir alle wissen, in jeder Turing-vollständigen Sprache theoretisch alles berechenbare. Die Frage ist, wie leicht es einem gemacht wird. Um CLs Makros zu ersetzen müsste das C-Makro-System auf dem AST arbeiten, also auf dem Code als geparste Struktur. Das ganze textuell zu ersetzen wäre zwar irgendwie möglich (Tcl erlaubt etwas ähnliches, wenn ich es richtig im Kopf habe), aber wirklich für alle praktischen Belange absolut nicht zu vergleichen.

Was die Metaprogrammierung in Lisp einfach macht ist, dass in Lisp Code und Daten gleich repräsentiert werden. (Nämlich als einfach verkettete Listen) Konkret bedeutet das: Lisp versteht Lisp, von Hause aus. Das ist der große Unterschied.

Natürlich stellen textuelle Makros, Template-Systeme und ähnliche Methoden eine Möglichkeit dar, ähnliches zu erreichen, aber mit steigender Komplexität verliert man sehr schnell die Lust (nehme ich zumindest an).

Das Problem dieser Systeme ist, dass sie immer nur ein Teil der Sprache sind, in Lisp ist es die Sprache selbst, mit der man arbeitet. Das ist absolut nicht zu unterschätzen, die gesamte Sprache steht auch in diesen Makros zur Verfügung, es ist keine ... sagen wir DSL wie das Template-System.

Um ein Beispiel zu nennen: Einen Lisp-Interpreter, oder von mir aus auch Compiler in Lisp zu schreiben ist mehr oder weniger eine Fingerübung (wenn wir von einem "kleinen" Lisp reden) für mich, wie sieht es da mit dem Durchschnitts-C++-Programmierer aus? Hat er die entsprechenden Werkzeuge zur Verfügung um so etwas leicht durchführen zu können? Wäre er froh, einen Parser für C++ in purem C++ schreiben zu dürfen? 🙂

Anhand Deiner Frage werde ich versuchen zu erläutern, warum CL-Makros meiner Meinung nach so wertvoll sind.
Zumal ich konkret in dem Beispiel jetzt keinen großen Gewinn im Vergleich zur OO von C++ sehe. Eine Initialisierung am Anfang und ein implizierter Desktruktor-Aufruf machen das gleiche; statt "file" muss man eben immer das Objekt angeben, was jetzt nicht weiter wichtig ist.
Zeig mir den Code, vor allem interessiert mich, wie beliebiger Code in einem Kontext laufen kann. Oder willst Du jedes mal eine neue Klasse erstellen? Wie wird der Code übergeben? Ich bin gespannt und lasse mich auch gern belehren.

Der Gewinn ist, dass jeglicher Lisp-Code durch Lisp bequem verwurstet werden kann. Die Betonung liegt auf bequem. Es ist absoluter Alltag, jeder Lisp-Programmierer ist, wenn man so will, ein Sprachdesigner. Davon abgesehen ist CLOS, das Common Lisp Object System sehr mächtig, aber das zu erklären würde vielleicht zu weit führen. Das eine schließt das andere nicht aus. (Vielleicht kommt mit der zweiten Aufgabe noch was nach, mal sehen) Außerdem gelten in Macros auch z.B. die Gültigkeitsbereiche und alles andere, alles ist transparent.
Ist es auch möglich Funktionen kontextabhängig zu überladen (ich weiß nicht, ob das die richtige Terminologie ist)?
Ich nehme an, Du meinst sie innerhalb eines Kontextes lokal zu binden. Es ist nicht nur möglich, sondern spielend einfach und absolut üblich.
Konkret schwebt mir da etwa Folgendes vor: Ich habe ne Datenbank-Anwendung und möchte so gut es geht, von der Datenbank abstrahieren. Wäre dann so etwas möglich:
(sqlite3 ("/pfad/zur/datenbank")
  …
  (query "select * from some_table")
  …
)
Also zunächst soll eine Datenbank-Verbindung aufgebaut werden und anschließend beziehen sich alle SQL-Queries auf diese Verbindung. Ob ich dabei ne Sqlite oder Mysql oder sonstiges benutze, sollte dementsprechend keine Rolle spielen, weil je nachdem die "query"-Funktion anders arbeitet.

Wäre das, oder sowas ähnliches, in Common Lisp möglich?
Also, ich zeige Dir die Macrology, aber ich implementiere hier jetzt nicht die einzelnen Funktionen, sondern zeige eher generell, wie so etwas geht.

Die Herangehensweise ist denkbar einfach; man fragt sich: Wie soll der generierte Code aussehen? In diesem Falle:
(let ((connection (make-connection "..."))
  (flet ((query (query-string)
           ...
           ...))
    <code>
    (close connection)))
make-connection sorgt für die Verbindung, der Stream wird an connection gebunden, dann wird lokal die Funktion query gebunden. Innerhalb des Gueltigkeitsbereiches dieser Bindung wird später der Code expandiert und am Ende die Verbindung geschlossen.

Was noch fehlt ist die sichere Trennung der Verbindung bei Fehlern, unwind-protect übernimmt das für uns (aehnlich zu try/finally):
(let ((connection (make-connection "..."))
  (flet ((query (query-string)
            ...
            ...))
    (unwind-protect
         (progn <code>)
      (close connection))))
Dies ist der Code, zu dem unser Makro expandieren soll. Von ihm aus gehen wir an die Sache heran. Da nun Code und Daten in Lisp ein und dieselbe Sache sind, setze wir praktisch diesen Code ähnlich einer Schablone in unsere Makrodefinition ein:
(defmacro with-sql ((path) &body body)
  `(let ((connection (make-connection ,path)))
     (flet ((query (query-string)
               ...
               ...))
       (unwind-protect
            (progn ,@body)
         (close connection)))))
Ob das wohl funktioniert? Herausfinden kann man es mit macroexpand, oder macroexpand-1 (pprint ist der pretty-printer):
DHL> (pprint (macroexpand-1 '(with-sql ("/foo/bar")
                               (foo)
                               (bar)
                               (query "select * from some_table")
                               (baz))))

(LET ((CONNECTION (MAKE-CONNECTION "/foo/bar")))
  (FLET ((QUERY (QUERY-STRING) ... ...))
    (UNWIND-PROTECT
        (PROGN (FOO) (BAR) (QUERY "select * from some_table") (BAZ))
      (CLOSE CONNECTION))))
; No value
DHL> 
Und ... ja, es funktioniert, der Code expandiert korrekt, with-sql ist nun Teil der Sprache, wie alles andere auch. Ein Großteil der meisten Implementationen ist bis auf ein paar sogenannte special-forms genau so programmiert, alles ist first-class.

(Anm: Zu beachten ist, dass in diesem Beispiel connection im Kontext sichtbar ist, dies nennt man Variable Capture und es kann eine Fehlerquelle darstellen, aber genauso, wie im Falle von query vollkommen beabsichtigt sein. Zu verhindern ist es durch die Verwendung von gensyms, doch dies Würde hier den Rahmen sprengen.)

Aufrufen kann man das ganze dann so:
(with-sql ("/foo/bar/baz")
  (foo)
  (print 'bar)
  (query "..."))
Was nun query an sich angeht, so gibt es viele Möglichkeiten. Du könntest normale funktionen query-foo, query-bar, query-... definieren und dem Makro ein weiteres Argument mitgeben, ein Symbol, welches die Verbindungsart bestimmt und in der Makrodefinition dann anhand dieses Symbols die passende dieser query-... eben an query Binden. Das aber ist nur eine Möglichkeit von vielen. Der Aufruf würde dann so aussehen:
(with-sql (mysql "/foo/bar/baz")
  ...
  ...)
Ob man es nun so mach, oder anders, ob man Klammern um die beiden Argumente haben will, oder nicht, ist jedermanns eigene Sache. Alternativ könntest Du beispielsweise auch einfach die entsprechende query-funktion mit übergeben.

Wichtig: Ich habe mir keine Implementationen angesehen, es mag sein, dass es viel elegantere Lösungen gibt, definitiv aber gibt es andere. Was ich getan habe ist lediglich, genau das, was du wolltest umzusetzen. Also bitte nur als proof-of-concept verstehen, nicht als einen ernstgemeinten Ansatz.
Vielen Danke danlei, für deine ausführliche Antwort. Ich habe natürlich keine Implementierung erwartet, sondern wollte tatsächlich nur ein proof-of-concept.

Mich hat das auf jedenfall überzeugt mir Common Lisp anzueignen. Die Sprache selbst syntaktisch anzureichern, klingt auf ziemlich vielversprechend; denn bis jetzt war ich mit keiner Sprache wirklich rundum glücklich.

Einzige Handycap wäre höchstens ein Mangel an Bibliotheken, aber ich meine mal gelesen zu haben, dass CL-Bindings zu C-Bibliotheken nicht schwer zu schreiben seien.
T.M. schrieb[Abschnitt zu C++ Metaprogrammierung]
Ok, aber bedenke, dass ich kein C++-Profi bin. Könntest Du mir dann vielleicht das Präprozessor-Äquivalent, oder eine Lösung basierend auf den anderen von Dir erwähnen Optionen zeigen? Vielleicht das SQL-Beispiel? Du bist nicht diskreditiert, wenn Du mir keinen Code lieferst, aber ich würde beides gerne im Vergleich sehen und mich dann über die Komplexität beider Ansätze und den damit verbundenen Aufwand relativ zueinander unterhalten.
[Schilderung des Arbeitsalltags, eher Pflege eines großen bestehenden Systems
Das leuchtet mir ein. Ich wage allerdings zu behaupten, dass auch Refactoring in gewissem Sinne in Lisp einfacher ist, aber ich sehe auch den Ausgleich durch modernere, auf große Projekte ausgelegte Systeme für C++. Meine Aussagen beschränken sich, auch da ich in deinem Arbeitsbereich keine Erfahrung habe, auf schnelles, effizentes Entwickeln, also sind wir und in diesem Punkt wohl weitestgehend einig. Die Frage ist vielleicht aber auch: Wäre ein Lisp-System gleicher Funktionalität überhaupt so groß? (Ich bin nicht sicher)
Der Ansatz von STL und boost, ich sag's immer wieder. C++ lernen sollte eigentlich heißen: die STL verstehen lernen.
Wieder würde ich gerne Code sehen, aber es muss nicht sein. Ich kann mir einfach nicht vorstellen, wie der von mir gezeigte Ansatz (in der demonstrierten Einfachheit) in diesen Systemen umzusetzen ist.

Ich bleibe bis auf weiteres bei meiner Behauptung, dass die von mir angesprochene syntaktische Abstraktion/Metaprogrammierung nach wie vor in C++ schwerer bzw. komplexer umzusetzen ist, als in einem Homoikonischen System. Ich lasse mich auch vom Gegenteil überzeugen.

Seien wir ehrlich, ich habe alles, was ich verlangt habe mit relativ minimalem Aufwand vorgelegt. Ich verlange nichts, was ich nicht selbst liefern kann.

Da Du allerdings weiter oben klargemacht hast, das deine Arbeit eine gänzlich andere als schnelle Entwicklung, sondern Pflege ist, kann ich nachvollziehen, dass diese erhöhte Komplexität in Deinem Falle vielleicht zu vernachlässigen ist. Die Frage ist: Wie würde sich das bei einem neu zu entwickelnden System verhalten? Welches System würde große Veränderungen leichter möglich machen?

Aber wir bewegen uns (ich zumindest) hier im Spekulativen.
Letzteres ist nichts als eine Mutmaßung. - Der Wert ist mir schon klar, nur ist es auch ein handfestes Problem. Es läuft auf Polymorphie in zwei orthogonalen Richtungen hinaus, wie gesagt: zwei unabhängige Klassenhierarchien und dann Zusammenführung durch Mehrfachvererbung, letzteres vielleicht in einem template, dann könnte man vielleicht dort eine generische Druckmethode machen. Ich würde dies allerdings, wenn's geht, vermeiden, zur Not sogar den OO-Ansatz verlassen. Die Frage ist, ob dies dann umständlich wäre.
Was wenn ich Dir sagte, dass ich das Problem in ein paar Zeilen Code lösen kann? Wir müssen hier keine Schwanzvergleiche machen, aber was ich sagen kann ist: Für CLOS ist dieses Problem keins. Was ich allerdings bemerke ist, dass Du das Problem sehr wohl verstehst und Ansätze lieferst. Das gelingt nicht jedem, den ich damit konfrontiere.
[Zum Exception-Handling]
Im CL Condition-System wird es folgendermaßen gehandhabt: Es gibt Conditions, Handler und Restarts. Unten im Stack werden die Restarts gebunden, es sind Optionen, wie nach dem Fehler fortzusetzen ist. Der Handler oben im Stack bestimmt, welcher Restart aufgerufen wird. Wird das Signal gesendet, gehts Stackaufwärts, denn da liegen die Funktionen, die am besten wissen, wie nach einem aufgetretenen Fehler fortzufahren ist (Woher sollte ein ... sagen wir read-line ganz unten wissen, wie der Chef bei einem Fehler weitermachen will?), ggf. kann auch ein Mensch der Handler sein, wenn kein Handler vorm Debugger etabliert wurde, welcher die letzte Instanz darstellt. Bindet der Handler die entsprechende Condition an einen Restart (die verfügbaren restarts sind natürlich zur Laufzeit abrufbar), so wird unten im Stack, also da, wo man am besten weiss, wie überhaupt die Funktion wieder aufgenommen werden kann, der entsprechende Restart aufgerufen und weiter gehts.
Was das Missionieren angeht, danke für die Mühe. Es ist ja nicht so, daß ich nicht auch links und rechts schauen würde. So bin ich ja auch zu C++ gekommen. Ich hab eigentlich mit Assembler angefangen, für zwei grundverschiedene Prozessoren, und erst, als ich an eine Produktivitätsgrenze stieß, wurde mir klar, es muß effektiver gehen. Aber eben diese Grenze ist bei mir hinsichtlich C++ noch längst nicht erreicht. C++ ist quasi mit mir mitgewachsen, in genau derselben Geschwindigkeit. Das Schauen links und rechts bleibt bislang Spiel, so interessant es sein mag.
Ich denke es gibt Sprachen, die man einfach kennen sollte, also über "den Namen habe ich schon einmal gehört" hinaus. Allerdings ist das mein persönlicher Maßstab und ich kann ihn anderen nicht aufzwingen. Was ich allerdings tue, ist es wärmstens zu empfehlen. Um welche Sprachen es geht, steht in meinem ersten Post in diesem Thread.
Kinch schriebVielen Danke danlei, für deine ausführliche Antwort. Ich habe natürlich keine Implementierung erwartet, sondern wollte tatsächlich nur ein proof-of-concept.

Mich hat das auf jedenfall überzeugt mir Common Lisp anzueignen. Die Sprache selbst syntaktisch anzureichern, klingt auf ziemlich vielversprechend; denn bis jetzt war ich mit keiner Sprache wirklich rundum glücklich.

Einzige Handycap wäre höchstens ein Mangel an Bibliotheken, aber ich meine mal gelesen zu haben, dass CL-Bindings zu C-Bibliotheken nicht schwer zu schreiben seien.
CL ist eine altehrwürdige Sprache deren Standard sehr, sehr gut durchdacht ist. Warzen gibt es viele, oft aus damals politischen Gründen. Trotzdem kann ich die Sprache empfehlen. Was Libs angeht, erwarte nichts in Richtung CPAN oder Python, aber andererseits: Es gibt Bibliotheken für Cl, die wirklich für sich schon ein Studium wert sind.

Was Bindings angeht, so ist CFFI Dein Freund.

Wenn Dir die CL-Community und das ganze drumrum zu unfreundlich ist (Es wird einem nichts geschenkt, es ist nicht wie bei ... Ruby, wo sich hunderte überschlagen, dir bei jedem Problem zu helfen), so sieh Dir Clojure an. Auch da ist Metaprogrammierung kein Problem, es läuft auf der JVM (also kein Problem mit Libs), die Community ist sehr hilfreich und Clojure bietet mit seiner STM ein System zur Nebenläufigen Programmierung, das seines Gleichen sucht.

Mein Traum wäre eine Mischung aus beiden, da CL doch nach wie vor "Lispiger" ist und was andere nicht mögen, die Warzen, schreckt mich schon lange nicht mehr ab.

(Nicht, dass ich jetzt hier Angst und Schrecken verbreite über die CL-Community, aber sie sind doch recht eigen. Jemand hat mal gesagt: CL Programmierer sind bornierte alte Säcke und ja ... es ist was dran. Allerdings sind es in der Regel (spreche nicht von mir) wirklich fähige Säcke; Du kannst sehr viel in kurzer Zeit lernen. Ich persönlich komm mit den CL-Leuten gut klar, vielleicht bin ich aber auch einfach assimiliert worden, kA.)
Danke für Warnung, aber ich komme eigentlich mit vielen Charakteren zurecht.^^

Aber noch eine Frage zum CFFI: Wenn ich meine Software verteilen will, muss dann auch CFFI auf dem Zielsystem installiert sein? Ich würde nach Möglichkeit gerne die Abhängigkeiten so gering wie möglich halten.
Kinch schriebDanke für Warnung, aber ich komme eigentlich mit vielen Charakteren zurecht.^^

Aber noch eine Frage zum CFFI: Wenn ich meine Software verteilen will, muss dann auch CFFI auf dem Zielsystem installiert sein? Ich würde nach Möglichkeit gerne die Abhängigkeiten so gering wie möglich halten.
Common Lisp Implementationen sind (analog zu Smalltalk) Image-basiert. Konkret heisst das: Du lädst alles was du brauchst in dein Image und wenn alles so läuft, wie Du Dir das vorstellst, dumpst Du. Das gesamte Laufzeitsystem plus alles zusätzlich von Dir geladene befinden sich in der ausführbaren Datei. Was nicht darin enthalten ist, sind die entsprechenden Libs, also die .so oder .dll Dateien. (Wobei das wohl mit ECL einem eingebetteten CL durch statische Kompilierung möglich wäre, aber damit habe ich persönlich keine Erfahrung.

Nachteile des Image-basierten Ansatzes sind ralativ große Executables in der Größenordnung von ein paar MB (je nach Implementation ab ca 5 MB würde ich schätzen, oder gegen 10-20, kommt drauf an.

Der Vorteil ist, dass das gesamte System lebt und live Debuggt werden, gepatcht werden und was-weiß-ich-nicht alles werden kann. Lisp ist dynamisch, auch als fertige Applikation.

Wenn man sich allerdings vor Augen hält was z.B. die Java Runtime auf die Waage bringt, sollte das mMn tolerierbar sein.

Alle guten CL Implementationen bieten das speichern von Images an, also lass dir nicht erzählen mit "CL kann man keine ausführbaren Dateien machen".

Was allerdings stimmt ist, dass man vollkommen anders entwickelt. Man arbeitet quasi am lebenden Programm, in einer Repl. Der Code wird (in allen ernstzunehmenden Implementationen) entweder zu Maschinen- oder Bytecode kompiliert.

Fürs Lernen an sich ist das alles jedoch eher unwichtig, aber trotzdem. Wichtig ist ein guter Editor, der beste ist (und um Lisp zu programmieren ist das jetzt keine Glaubensfrage, sondern schlicht Fakt) der Emacs. Emacs+Slime ist die Standard-IDE und ich würde es empfehlen, sich damit anzufreunden, auch wenns schwerfällt. Ansonsten blieben AllegroCL und LispWorks. Beide kommen (zumindest auf Win) mit einer IDE, aber die kostenlosen Versionen sind arg beschnitten.

Ich empfehle SBCL oder CCL als gute Implementationen und Emacs+Slime als IDE.
Hey, neuerlich danke für die Ausführung.

Im Moment lerne ich mit dem "clisp". Kann ich dich fragen, was der Vorteil von sbcl gegenüber clisp wäre?

10 - 20 MB sind schon habbig, was die Startzeit angeht, aber wäre mir wirklich lieber als ne komplette JVM als Abhängigkeit (abgesehen von der Größe der JVM bin ich froh nichts mehr mit Java zu tun haben zu müssen).

Kennst du gute Ressourcen zum Einsteigen? Ich arbeite gerade das hier durch: http://gigamonkeys.com/book/

Und sorry fürs okkupieren des Thread The_Muh.
Kinch schriebHey, neuerlich danke für die Ausführung.

Im Moment lerne ich mit dem "clisp". Kann ich dich fragen, was der Vorteil von sbcl gegenüber clisp wäre?

10 - 20 MB sind schon habbig, was die Startzeit angeht, aber wäre mir wirklich lieber als ne komplette JVM als Abhängigkeit (abgesehen von der Größe der JVM bin ich froh nichts mehr mit Java zu tun haben zu müssen).

Kennst du gute Ressourcen zum Einsteigen? Ich arbeite gerade das hier durch: http://gigamonkeys.com/book/

Und sorry fürs okkupieren des Thread The_Muh.
SBCL kompiliert zu nativem Code, Clisp zu ByteCode. SBCL ist eine sehr performante Implementierung und auch diejenige, welche am aktivsten entwickelt wird, soweit ich das beurteilen kann. Außerdem bietet dir SBCL sehr gute Typenableitung und ist sehr streng. Du genießt praktisch die Vorteile statischer Typisierung, ohne den Preis dafuer bezahlen zu müssen. In CL sind Typdeklarationen optional und werden in der Regel eher als Werkzeug zur Optimierung verstanden, sie können aber auch in Implementationen wie SBCL sehr gut zu Sicherheitszwecken benutzt werden. Weiters ist SBCL wohl die Implementation, die am besten von Slime unterstützt wird. Außerdem stand Clisp, zumindest früher, im Ruf, nicht gerade die konformste Implementation zu sein. Den momentanen Status kenne ich allerdings nicht, da ich Clisp so gut wie gar nicht benutze, lediglich um Code auf verschiedenen Implementationen zu testen, oder Verhalten zu vergleichen.

CCL hat den vorteil sehr schnell zu kompilieren, was mir persönlich sehr gut gefällt.

Ich rate nochmals wirklich dringend davon ab, Lisp auf der Kommandozeile zu benutzen, oder gar, wie in C &Co Dateibasiert zu arbeiten. Slime, oder eine gleichwertige IDE ist wirklich in diesem Falle kein Luxus, sondern gehoeren dazu. Glaub mir. Nur in einer solchen Umgebung laesst sich wirklich optimal arbeiten. (Also, jeder kann machen was er will, aber meinen Rat kennst jetzt.)

Wenn Du im IRC unterwegs bist hast übrigens gute Chancen mich auf #lisp@freenode anzutreffen, ich helf gern weiter, wenns irgendwo hakt. (Und da sind auch wirklich fähige Leute, vor allem treiben sich sehr viele SBCL-Entwickler da rum.)

ED @ Ressourcen

Mit PCL machst Du schonmal nichts falsch, ansonsten:

Sehr gut für absolute Anfänger ist:
http://www.cs.cmu.edu/~dst/LispBook/book.pdf

Auch empfehlenswert:
http://www.psg.com/~dlamkins/sl/contents.html

Auch interessant ist Grahams On Lisp, wobei er einen eher eigenbrödlerischen Stil hat (eher wie ein Schemer). On Lisp behandelt Macros im speziellen, ist also von der Warte aus interessant:
http://www.paulgraham.com/onlisp.html

Soviel zu den im Netz verfügbaren freien Büchern, mein absoluter Liebling ist PAIP von Peter Norvig. Nicht durch den Titel täuschen lassen, das Buch ist wirklich nicht nur für AI-Interessierte, sondern insgesamt ein ... mMn Meisterwerk.

Winston & Horns Lisp (3rd ed.) ist auch ok, ebenfalls Grahams ANSI Common Lisp.

Speziell zu CLOS ist Keenes Object Oriented Programming in Common Lisp interessant und natürlich The Art Of The Metaobject Protocol.

Zur Implementierung kann ich dann noch LiSP (Lisp in Small Pieces) empfehlen, für den Einstieg natürlich eher nichts.

Dann kannst Du noch nach lispm und lispcast Videos googlen.
Ok, danke dir soweit. Ich muss mal schauen, ob ich mich mit Emacs anfreunden kann. Ich bin eher der vim'ler und bei Emacs hatte ich immer die Sorge bei zu intensiver Nutzung vorzeitig Arthritis zu bekommen.

Ich werde mich dann mal ein wenig in Lisp anlesen. Schönen Abend noch.

Edit: Sehe die Ressoucen gerade. Vielen Dank, wird den Einstieg sicherlich schneller machen.
Kinch schrieb Edit: Sehe die Ressoucen gerade. Vielen Dank, wird den Einstieg sicherlich schneller machen.
Gerne. An guter bis brillanter Literatur mangelt es wirklich nicht. 🙂

Achso, das wichtigste vergessen, das Hyperspec:
http://www.lispworks.com/documentation/common-lisp.html

und CLtL2:
http://www.cs.cmu.edu/Groups/AI/html/cltl/cltl2.html

Das HyperSpec ist quasi eine Online-Version des Standards, CLtL2 ist pre-ANSI, nicht 100%ig konform, aber wird von vielen als zugänglicher empfunden.

Und übrigens: Auch ich war überzeugter Vi/Vim-User, bis ich das Licht gesehen habe. 😉