[linux-l] Mächtigkeit von LISP (war: Geschwindigkeit von DB)

Volker Grabsch vog at notjusthosting.com
Fr Okt 13 13:35:39 CEST 2006


On Sun, Jul 02, 2006 at 01:35:24AM +0200, Oliver Bandel wrote:
> > > Allerdings gab es Lisp doch früher nur in kommerziellen Umgebungen,
> > > und da bekommen die dann eben auch entsprchend gute Tools geliefert.
> > > *Da* lohnt sich das Geld ausgeben noch ;-)
> > 
> > Ja, mag sein. Aber Gutes Zeug[tm] wird von der OpenSource-Community doch
> > immer gern übernommen. Auch gute freie XML-Editoren gibt es.
> 
> Naja, vielleicht klappt das ja nicht immer.
> Dafür ist Lisp eben in der OpenSource-Community zu sehr Aussenseiter...
> ...die paar, die sich mit Lisp intensiv beschäftigen optimieren vielleicht
> lieber die Compiler und Bibliotheken?!

Auf Common Lisp mag das zutreffen, aber Scheme z.B. ist ein (nicht zu
unrecht) sehr beliebter Dialekt. Quasi die leichtgewichtige Variante.

Man findet im Netz Leute, die Scheme hassen, und welche, die es lieben.
Genauso wie bei Java und anderen Sprachen. Freie Scheme-Implementierungen
gibt es ebenfalls, und etliche Artikel z.B. im LinuxMagazin.

Es gibt eine hervorragende Einführung in das Programmieren:

    How to Design Programs
    - An Introduction to Computing and Programming
    http://www.htdp.org/2003-09-26/Book/

in der vorallem einfache Konzepte erklärt werden, wie z.B. die
Aufteilung eines Problems in Teilprobleme. (Etwas, das bei uns an der
Uni selbst einige aus den späten Semestern nicht hinkriegen. Grausamer
Spaghetti-Code ist die Folge.)

Wie auch immer, das Buch verwendet nicht Python, sondern Scheme, und
schwerer lesbar finde ich es nicht. Der Quellcode ist etwas schwerer
lesbar, aber lange nicht so komplex wie es z.B. entsprechender Java-
Code wäre. Scheme ist wie Python und Ruby eine Sprache, in der man sich
auf die Konzepte in Ruhe konzentrieren kann.

Schon allein wegen solcher Bücher würde ich Anfängern zu einer
bestimmten Sprache raten. Wegen einiger sehr guter für nicht-programmierer
geeigneten Tutorials habe ich früher immer Python empfohlen, heutzutage
würde ich wegen obigem Buch auch Scheme empfehlen.

Selbst, wenn jemand komplexere Excel-/Visual Basic-/sonstawas-Scripte
entwerfen muss, liefert obiges Buch genug Techniken, um auch so jemanden
anzu zu befähigen, zu wissen, was er tut. Und nicht wahllos riesigen
Spaghetti-Code zu produzieren, der immer größer wird.

> Anderer Grund: compiletime-Prüfungen der Typen.
> Wenn man nicht unbedingt dynamische Eigenschaften einer Spraqche braucht,
> oder diese nicht *hauptsächlich* für ein Projekt braucht, sondern nur
> für ein paar Teilbereiche, dann bringt strikte Typprüfung zur Compilezeit
> den Vorteil extrem verringerter Debugging-Zeiten.

Das ist wahr. Lustig an obiger Einführung ist übrigens, dass dort unter
anderem Empfohlen wird, für jede Funktion einen "Vertrag" zu schreiben.
Das heißt:
* Was soll sie tun?
* Welche Eigenschaften haben die Eingabe-Parameter?
* Welche Eigenschaften haben die Ausgabe-Parameter?

Dies alles wird in Kommentaren festgehalten, während in statisch
typisierten Sprachen, vorallem in C++ und Ocaml, die letzten beiden
Dinge in der Programmiersprache festgelegt werden.

(nicht durch die eigentliche Funktion, aber durch die Signaturen,
also den Ocaml-"Interfaces" bzw. den C++ - "Headern")

Dennoch können auch die Typsysteme nicht alle Eigenschaften erfassen.
Klassisches Beispiel: "Einfügen in eine sortierte Liste"
    (http://www.htdp.org/2003-09-26/Book/curriculum-Z-H-16.html#node_sec_12.1)

In den Worten von Ocaml: Die Signatur ist nicht nur

    'a list -> 'a -> 'a list

sondern die eigegebene Liste muss sortiert sein. Die ausgegebene
Liste ist wieder sortiert.

Es gibt Sprachen, die auch das schaffen, z.B. Eiffel mit seinen
precondition/postcondition, die beliebig komplex werden können. Das
sind nicht einfach nur "assertions". Nein, diese Invarianten werden
vom Compiler überprüft.

Insofern hat Eiffel auch Ocaml etwas voraus. Andererseits gilt Eiffel
als imperative/OO - Sprache, daher weiß ich nicht, ob es bei anderen
Themen wieder hinten an steht.

Aber selbst, wenn ich die Semantik "kriegt sortierte Liste, liefert
sortierte Liste zurück" direkt in der Programmiersprache ausdrücken
kann, gibt es vielleicht Vor- und Nach-Bedingungen, die man trotzdem
nicht direkt ausdrücken kann und die nur in Form von Kommentaren
stehen.  (oder sie sind so komplex, dass ein paar Wort im Kommentar
vollkommen ausreichen, und eine vollständige Spezifikation das
Aufwand/Nutzen-Verhältnis nur schmälern würde)

> Besser wäre halt neu schreiben - aber was, wenn da so viele Fallstricke
> sind, daß man statt einigen zehn oder einigen Hundert Zeilen dann
> bei der Änderung feststellt, daß die gesamte Codebasis - die ja wesentlich
> umfangreicher sein kann - marode ist?
> Und daß man dann wirklich mehr kaputt macht als man gerade biegen kann?
> Dann müsste *alles* bearbeitet werden.... => rewrite from scratch!

Nicht unbedingt. Im Studium haben wir gelernt, dass "Rewrite from Scratch"
in der Regel noch schlimmeren Code produziert, wenn der alte Code nicht
verstanden wurde. Insbesondere, wenn die Dokumentation fehlt (was bei
einer maroden Codebasis praktisch immer gegeben ist ;-)), dann ist man
beim Rewrite dazu verdonnert, über die selben subtilen Problemchen zu
stolpern wie auch bei der Entwicklung des alten Codes.

Wie der Name schon sagt, ist ein Rewrite viel doppelte Arbeit. Es ist
schwieriger, aber viel effizienter, wenn man den alten Code komplett
redesignt. Wir haben als Alternative zu den beiden Extremen einen sehr
interessanten Mittelweg kennengelernt, die "Software-Restauration".

Das heißt, man analysiert den alten Code und überlegt sich eine sinnvollere
Aufteilung in Module / Klassen / Funktionen. Dabei muss man nicht bis in
die letzten Details gehen. Man legt für jedes Modul die Schnittstelle
"nach außen" fest, nicht die innere Struktur.

Statt diese neue Struktur aber nun direkt mit neuem Code zu befüllen,
implementiert man sie durch Wrapper. Man ruft also den alten Code auf.
Manche Sachen rufen direkt eine alte Funktion auf, andere müssen ein
einfaches Zusammenspiel von den alten Funktionen hinkriegen. Egal wie,
man hat damit ein Fundament. Eine ordentliche Schnittstelle ("Ordnung"),
aber immer noch die Bugs des alten Codes. Gegen diese neue Schnittstelle
kann man nun Tests entwickeln, und man kann sie sauber dokumentieren, was
vorher im Chaos des alten Codes kaum möglich war.

Bisher haben wir lediglich Zusatz-Entwicklungen. "Yet another layer",
wenn man so will. Aber mithilfe dieses Layers können wir uns nun an
den Haaren aus dem Sumpf ziehen. Der alte Code wird Stück für Stück
"umgeformt". Jede alte Funktion soll nun den neuen Layer benutzen.
Dabei kann man sich zuerst die leichten Dinge heraus picken und dann
immer größere Brocken hernehmen. Auch hier hat man vergleichsweise
wenig Aufwand, man muss nichtmal wirklich die Funktionen verstehen.
Es ist mehr wie aquivalentes Umformen von Gleichen (nur, dass man
Algorithmen umformt). Die Testsuite passt währenddessen auf, dass
man keine neuen Bugs hinzufügt. Die Funktionen vereinfachen sich
dabei Stück für Stück, sodass man sie irgendwann *tatsächlich* verstehen
kann.

Bietet sich nebenbei die Gelegenheit, einen Bug zu entfernen,
ergreift man diese natürlich. Meistens führt das Bugfixen sogar zu
noch einfacheren Code. (... weil man z.B. eine misslingene Code-Duplikation
ersetzt gegen den Aufnruf einer Funktion, die genau den Zweck dieses
Code-Stücks erfüllt und zudem weniger Bugs hat).

Das ganze ist ziemlich "straight-forward" und sehr gut parallelisierbar.
Man kann Leute zu gleichen oder unterschiedlichen Zeitpunkten an
verschiedene Funktionen oder Module setzen. Jeder formt erstmal nur um.
War das neue Interface "geeignet", vereinfachen sich die Funktionen
dabei. Irgendwann sind Teile der alten Code einfach genug, um direkt
das neue Modul zu kommen. Dabei werden entweder alte Funktionen
zusammengefasst oder Funktionen, die immer noch zu groß sind, aufgeteilt.
Das heißt, auch "innen" in den Modulen herrscht dann Ordnung.

Konkret in der HU-Berlin lief ein Software-Redesign der "CTX", einer
vom Physik-Institut entwickelten, historischen Steuerungs-Software
für gewisse Mess-Aparaturen. Die Physik kam auf die Informatik zu,
weil sie den Code nicht mehr pflegen konnten, und baten um Hilfe.
Daraus entstand ein große Projekt, das sich über viele Semester
hinzog, wo viele Studenten versetzt ca. 2-3 Semester daran arbeiteten,
einige machten sogar "große" Analyse- und Refaktorisierungs-Arbeiten,
sodass es für mehrere Diplom-Arbeiten pro Semester reichte.

Diese Methode war nicht nur wesentlich weniger aufwändig als eine
Neu-Implementierung. Sie garantiert auch eine bessere Qualität, weil
der Code jederzeit lauffähig bleibt und getestet wird. Zudem ist
das sehr gut parallelisierbar. Die meiste Zeit spart man IMHO dadurch,
dass man vom alten Code "lernt" (denn da hat sich jemand was dabei
gedacht!), statt sich *alles* nochmal neu überlegen zu müssen.

Auch in der Industrie ist das wohl sehr beliebt und auf jeden Fall
effizient.

> DAS spricht IMHO für funktionale Sprachen: Nebeneffekte ade.
> Und für statische Typisierung: das Interface steht, und wenn die
> Implementierung geändert wird, würden bei versehentlicher
> Typ-Änderung die Sourcen gleich nicht akzeptiert werden!
> 
> Ich denke sogar, daß in dem Projekt, das Du da oben nanntest,
> die meisten Probleme dadurch entstanden sind, daß es eben keine
> so rigide Typprüfung wie z.B. i OCaml gibt!

Das stimmt nicht immer. Es gibt auch viele grundsätzliche Probleme,
die bei jeder Programmiersprache entstehen können. Was ist, wenn
man keine Interfaces verwendet? Was ist, wenn man riesen-Funktionen
hat? Was ist, wenn man typisch funktionale Probleme iterativ löst
oder andersrum? Überhaupt, was ist, wenn man's einfach nur zu
umständlich macht? Sei es durch zu wenige, riesige Funktionen oder
durch zu viele fast triviale Funktionen?

Ein guter Compiler hilft sehr, auch was den Programmier-Stil angeht.
Aber eine sinnvolle Zerlegung in Teilprobleme setzt ein gutes
Verständnis des Anwendungsgebietes voraus.


Viele Grüße,

    Volker

-- 
Volker Grabsch
---<<(())>>---
Administrator
NotJustHosting GbR



Mehr Informationen über die Mailingliste linux-l