Posts tagged ‘Lisp’

S.O.L.I.D. for dynamic and functional languages at SoCraTes 2011

At the SoCraTes 2011 conference I facilitated a session about applying the S.O.L.I.D. design principles to dynamic OO languages and to functional languages.

I prepared some Flipcharts with the SOLID principles (Slideshare), code examples for Java, Ruby, Scala and Clojure (GitHub) and a paper with a description of the SOLID design principles and some thesis about their application to dynamic OO and functional languages (PDF).

I am not sure if the session can be counted as a success. We had three groups. One was working on the application of SOLID to dynamic OO languages with the Ruby code examples. The second group was working on the application of SOLID to statically typed functional languages with the Scala code examples. The third group worked on the application of the SOLID principles to dynamic functional languages with the Clojure examples.

The results were ambiguous and we didn’t find final answers. What became clear is that the SOLID design principles can be applied to all these types of languages. What stayed fuzzy was the question if that application would be useful for the everyday work with these languages. While I tend to think that in fact not every principle is useful in every languages there were others having the opposite opinion.

One of the problems within the workshop was that today OO thinking is baked into out heads. And so the Scala and the Clojure group created designs in OO style. And if you do that with a functional language of course SOLID is applicable and at least to a certain degree useful 🙂

In the end of the session we had a discussion in a smaller group of people if it may be to misleading to apply SOLID to functional programming. Perhaps it drives our thinking in the wrong direction. Perhaps it would be better to go back to the underlying principles of cohesion and coupling and creating new design principles for functional languages on top of these. And perhaps such design principles already exist and I am just to blind to see them.

So: There is still a lot to learn and a lot of experiments to do. If you like to contribute I would really much appreciate it.

 

 

September 9, 2011 at 3:12 pm 2 comments

Buchtipp: Practical Clojure

Wenn ich mit meinem vorigen Blogpost den einen oder anderen neugierig auf Clojure gemacht habe, gibt es hier gleich noch das passende Buch. “Practical Clojure” von Luke VanderHart und Stuart Sierra führt prägnant und kurz in die Sprache Clojure und die zugehörigen Bibiotheken ein. Das Thema Parallelität mit Clojure wird ausführlich behandelt.

Das Buch kommt schnell auf den Punkt. Für jemanden, der Lisp noch nie gesehen hat und für den funktionale Programmierung ganz neu ist, könnte das Buch evtl. etwas schwer verständlich sein.

 

November 22, 2010 at 7:26 pm Leave a comment

Clojure lernen

Um gedanklich nicht einzurosten, sehe ich mir dann und wann eine neue Programmiersprache an. Diesmal war Clojure an der Reihe. Clojure ist eine funktionale Sprache, die auf der Java VM läuft. Und letztlich ist Clojure nicht wirklich eine neue Programmiersprache, sondern ein Lisp-Dialekt. Clojure bietet an der einen oder anderen Stelle syntaktischen Zucker, so dass der Quelltext etwas aufgelockerter aussieht (nicht soviele runde Klammern) und dadurch für Lisp-Neulinge leichter lesbar ist. Ansonsten ist Clojure aber ein vollständiges Lisp, das sich im Gegensatz zu Common Lisp aber zu beschränken weiß: kein Mega-For-Konstruktur, kein CLOS, etc. Und einige Antiquitäten sind moderneren Einrichtungsmöbeln gewichen: car heißt jetzt first und cdr heißt tail.

Ich finde, das Resultat ist eine einfache, fokussierte Sprache, die einfach zu lernen ist und Spaß macht. Dazu trägt auch bei, dass Clojure bewusst funktional ist und kein OO-Hybrid. So wird man als OO-Entwickler gezwungen, sich einer anderen Perspektive beim Programmieren zu öffnen und das ist auf jeden Fall nützlich – und sei es nur zur Horizonterweiterung.

Neben der eigentlichen Sprache bietet Clojure einige eigene Bibiotheken, insbesondere für die Programmierung paralleler Algorithmen. Durch die funktionale Struktur von Clojure-Systemen gestaltet sich dieses Themengebiet in Clojure viel einfacher als z.B. in Java. Ich bin zwar der Meinung, dass parallele Algorithmen im Moment deutlich überschätzt sind. Aber wenn man meint, dass man es braucht, dann sollte man sich Clojure auf jeden Fall mal ansehen.

Und wenn man keine parallelen Algorithmen braucht, sollte man sich Clojure auch ansehen und z.B. die eine oder andere Code-Kata in Clojure programmieren. Es hat schon seinen eigenen Charme, vollständig funktional zu programmieren.

 

 

November 22, 2010 at 7:19 pm 3 comments

Terminplaner in Prolog

Ich habe meine Prolog-Kenntnisse aufgefrischt und dazu das Terminplaner-Beispiel in Prolog implementiert. Die geringe Anzahl Lines of Code (LoC) der Prolog-Lösung finde ich ziemlich beeindruckend. Ich habe die Größe der Lösung in den verschiedenen Programmiersprachen mal gegenüber gestellt (LoC: ohne Leerzeichen und Kommentare).

Sprache LoC LoC Tests
Java 187 109
Groovy 155 79
Ruby 141 90
Lisp (CLOS) 63 77
Prolog 34 93

LoC ist sicher kein besonders gutes Maß, um die Mächtigkeit einer Programmiersprache zu bewerten. Wenn man allerdings nur 34 Zeilen braucht, wo man sonst 150-200 Zeilen braucht, finde ich das schon ziemlich denkanstößig.

Der Code steht unter github bereit: http://github.com/stefanroock/Terminplaner/tree/master (Achtung: Die Python-Lösung funktioniert noch nicht).

July 13, 2009 at 7:30 pm 5 comments

Buchtipp: Practical Common Lisp

Peter Seibel führt mit Practical Common Lisp anhand praktischer Beispiele (Unit-Testing, MP3-Dateiparsing, Web-Anwendungen etc.) in Common Lisp ein und zeigt damit eindrucksvoll, dass Common Lisp keineswegs auf künstliche Intelligenz oder funktionale Programmierung beschränkt ist.
Das Buch gehört zu den sehr wenigen IT-Büchern, bei denen das Lesen Spaß macht.

Auch wenn man selbst nicht mit Common Lisp arbeiten wird, öffnet das Buch ganz neue Perspektiven. Es ist schon erstaunlich, wie weit Common Lisp seiner Zeit voraus war und noch ist. Ich kenne keine andere Programmiersprache, die so mächtige Features hat wie Common Lisp.

Das Macro-System (nicht zu vergleichen mit dem C-Macros) erlaubt die Definition eigener Sprachen in Common Lisp – ein Ansatz, der heute als DSL (Domain Specific Language) bekannt ist und mit vergleichsweise wahnsinnig hohem Aufwand und Code-Generierung betrieben wird.

So kann man eigentlich bei jedem Feature anderer Programmiersprachen sagen: “Das hat Common Lisp auch, nur mächtiger und flexibler.” Da stimmt es mich schon etwas traurig, dass sich Common Lisp nicht in der Breite durchgesetzt hat und man heute noch mit vergleichsweisen Krücken unterwegs ist.

Da drängt sich natürlich die Frage auf, warum wir dann nicht einfach alle in Common Lisp programmieren. Eine Antwort gibt Paul Graham.

Dem habe ich noch hinzuzufügen, dass Common Lisp zwar unbestreitbar sehr mächtig ist, aber auf eigenartige Weise etwas schief zusammengesetzt wirkt. Das ist aber kein wirklich starkes Argument, weil mit Scheme und NewLisp weitere Lisp-Dialekte zur Verfügung stehen.

March 6, 2007 at 7:07 pm 1 comment

Lisp vs. XML

Ein ebenso beliebtes wie altes Argument gegen Lisp sind die Klammerungen: “Durch die vielen Klammernist der Code unleserlich.”

Interessanterweise stören sich dieselben Leute aber nicht an XML. Oder wollen die allen Ernstes behaupten, dieser XML-Code


<html>
<body>
<h1>Eine Überschrift</h1>
<p>Findest Du mich zwischen all dem XML?</p>
</body>
</html>

sei besser lesbar als seine Entsprechung in Lisp


(html
(body
(h1 "Eine Überschrift")
(p "Ich bin ganz leicht zu finden!")))

?

(Für die Erbsenzähler: Der XML-Code benötigt 50% mehr Zeilen und 30% mehr Tastenanschläge – ohne Einrückungen).

Tse, tse…

January 30, 2007 at 8:17 am Leave a comment

Ruby goes Lisp

Mit ParseTree kann man zur Laufzeit die Struktur eines Ruby-Programms analysieren. Das was da rauskommt sieht sehr nach Lisp-S-Expressions aus, was Grahams These stützt, die Programmiersprachen würden sich mit der Zeit immer mehr Lisp annähern. Dass mit ParseTree bei weitem nicht die Flexibilität von Lisp erreicht ist, kann man auf der ParseTree-Homepage lesen: Es funktioniert nicht für die Ruby-Core-Klassen und verändern bzw. generieren kann man so einen ParseTree wohl auch nicht. Dennoch wahrscheinlich eine sehr nützliche Geschichte.

December 28, 2006 at 6:18 pm Leave a comment

Terminplaner in Lisp

Ich habe das Terminplaner-Beispiel zuerst in Java programmiert und dann nach Groovy (Skriptsprache für die Java-Plattform) programmiert. Das Beispiel ist in Groovy als eine der jüngsten Programmiersprachen doch erheblich kürzer und einfacher als in Java. Da hat es mich dann doch gereizt, das Beispiel auch einmal in einer der ältesten Programmiersprachen zu programmieren: Lisp. Dabei habe ich Common Lisp verwendet mit CLOS (Common Lisp Object System) und Lisp-Unit von Chris Riesbeck.

Die quantitativen Ergebnisse aller drei Implementationen zuerst einmal im Vergleich:

Java

  • 5 Fachklassen, 1 Exception-Klasse, 1 Testklasse
  • 37 Methoden inkl. Konstruktoren + 10 Methoden in Testklasse
  • 246 LOC operativ + 144 LOC für Testklasse (inkl. Leerzeilen)
  • 4 For-Schleifen, 4 If-Abfragen

Groovy

  • 4 Fachklassen, 1 Exception-Klasse, 1 Testklasse
  • 30 Methoden inkl. Konstruktoren + 9 Methoden in Testklasse
  • 203 LOC operativ + 123 LOC für Testklasse (inkl. Leerzeilen)
  • Alle 4 For-Schleifen der Java-Lösung wurden durch Closures ersetzt, die 4 If-Abfragen blieben bestehen.

Common Lisp

  • 4 Fachklassen, 1 Testklasse – Exceptions werden mit Hilfe von Conditions modelliert (nur eine Zeile notwendig)
  • 17 Methoden, 1 Condition-Funktion, 10 Testmethoden
  • 76 LoC operativ, 100 LoC Test (inkl. Leerzeilen)
  • Alle 4 Schleifen der Java-Lösung wurden durch Closures ersetzt, die 4 If-Abfragen blieben bestehen, allerdings in den spezialisierten Varianten when und unless.

Wow! Die Common-Lisp-Variante hat erheblich weniger Code als die Java- oder die Groovy-Variante. Woran liegt das?
Naheliegend wäre es, die Ursache bei dem Lisp-Feature zu suchen, dass keine andere Programmiersprache anbietet: Macros (Vorsicht: Lisp-Makros sind ganz anders als die berüchtigten C-Makros). Dem ist aber nicht so. Tatsächlich habe ich in dem Lisp-Code kein eigenes Makro selbst definiert und nur an einer Stelle von der Mächtigkeit der
Lisp-Makros profitiert. Mit assert-error aus Lisp-Unit kann man sehr einfach prüfen, ob eine Exception (im Lisp-Jargon Condition) geworfen wird:


(define-test benutzer-nicht-eingeladen-exception
(setup)
(let ((termin (make-termin stefans-kalender ein-datum 180 "TDD-Dojo")))
(assert-error 'benutzer-nicht-eingeladen (lehne-termin-ab termin "Henning"))))

Zum Vergleich der entsprechende Java-Code:


public void testBenutzerNichtEingeladenException() {
Termin termin = _stefansKalender.newTermin(_jetzt, 180, "TDD-Dojo");
try {
termin.lehneTerminAb(HENNING);
fail("BenutzerNichtVorhandenException erwartet");
} catch (BenutzerNichtEingeladenException e) {
assertTrue("Exception erwartet", true);
}
}

Das erklärt aber nicht, warum der operative Code in Lisp so viel kürzer ist als in Java oder Groovy. Eine nähere Analyse fördert folgende Gründe zu Tage:

  • In Java und Groovy werden eigene Zeilen spendiert, für schließende geschweifte Klammern. Für das Lisp-Äquivalent (schließende runde Klammern) werden keine eigenen Zeilen spendiert. Dierk König schlägt in seinem kommenden Groovy-Buch diese Art der Formatierung für Groovy für bestimmte Situationen auch vor (bei den Buildern).

    • In Java und Groovy ist es üblich, Leerzeilen in Methoden einzufügen, teilweise auch zwischen Attributen. Beides macht man in Lisp eher nicht.
    • Das aufschreiben von Attributen einer Klasse ist in Common-Lisp schlanker als in Groovy oder Java. Insbesondere gibt es prägnante Abkürzungen, um Setter und Getter und Defaultwerte zu definieren.
    • Einige Implementierungen lassen sich nicht sinnvoll 1:1 nach Common-Lisp transferieren. Daher vergleicht man ein Stück weit dann doch Apfelsinen mit Orangen (so groß wie zwischen Äpfel und Birnen sind die Unterschiede dann doch nicht 🙂

    Der gewaltige Unterschied in den LoC findet sich bei der Menge der Syntaxelemente in deutlich reduzierter Form. So spart man sich in Common-Lisp zwar Einiges an Zeilen für die Attributdefinitionen in Klassen, es gibt aber die gleiche Anzahl von Attributdeklarationen. In diesem Sinne ist die Lisp-Lösung zwar kürzer als die Lösungen in Java und Groovy. Sie ist bzgl. der Komplexität aber äquivalent zur Groovy-Lösung und diese beiden Lösungen sind weniger komplex als die Java-Lösung.

    Wieviel wirkt der große Unterschied in den LoC? Quelltext wird viel häufiger gelesen als geschrieben. Daher ist die Einsparung der Tastenanschläge bei der Code-Erstellung nicht wirklich von Interesse. Wenn man allerdings beim Lesen von Quelltext weder vertikal noch horizontal scrollen muss, erleichtert dies das Lesen: Im Lisp-Terminplaner passt jede operative Klasse problemlos auf eine Bildschirmseite, so dass man jede Klasse auf einen Blick erfassen kann. Das kann man als (leichten) Vorteil von Lisp werten. Allerdings muss ich zugeben, dass man den Groovy-Code an der einen oder anderen Stelle noch etwas eleganter Formulieren kann und dann auch noch etwas an LoC einsparen kann.

    Quelltext des Terminplaners in Common-Lisp.

    September 22, 2006 at 7:35 pm 2 comments

  • IDEs und der K(r)ampf zwischen Statik und Dynamik

    Für Java existieren heute mit Eclipse, IntelliJ IDEA und Konsorten sehr mächtige Entwicklungsumgebungen. Cross-Referencing, Refactoring und Auto-Completion sind Features, die man nicht mehr missen möchte.
    Dass diese Features für die aktuellen Skriptsprachen a la Python, Ruby oder Groovy nicht oder nur eingeschränkt existieren, wird häufig als Argument gegen diese Skriptsprachen verwendet.

    Aber wird über diese Features wirklich reflektiert?

    Cross-Referencing
    Mit den Cross-Referencing-Möglichkeiten der IDE kann man auf Tastendruck oder Mausclick von einer Codestelle zum referenzierten Element gelangen (z.B. von einer Variablendeklaration zur Klasse, von deren Typ die Variable ist).

    Natürlich sind die Cross-Referencing-Möglichkeiten in den IDEs deutlich mächtiger als eine Textsuche über Dateien. Aber ist der Unterschied wirklich so groß? Und ist die Textsuche häufig nicht sogar schneller?

    Refactoring
    Refactoring ist eine der großen Errungenschaften in der Neuzeit der Softwareentwicklung und ihr Automatisierungsgrad ist wirklich erstaunlich hoch. Aber welche automatisierten Refactorings nutzen wir überhaupt bei der täglichen Arbeit und wie häufig? Viele Entwickler verzichten sogar absichtlich auf bestimmte einfache Automatisierungen (z.B. “Introduce Variable”), weil sie von Hand schneller sind – die Mächtigkeit der IDEs führt bei großen Projekten leider dazu, dass die Arbeit mit ihnen mitunter doch etwas zäh wird.
    Viele Refactorings kann man in der Tat mit vertretbarem Aufwand und Risiko auch von Hand durchführen. Einige andere (z.B. “Rename Class”) lassen sich auch auf anderem Wege automatisieren (Find&Replace über Dateien). Natürlich kann man sich dann oft nicht 100%ig sicher sein, dass alles korrekt geändert wurde. Man muss anschließend die Tests ausführen. Dass muss man in Java aber auch, weil es immer mehr untypisierte Referenzen in XML-Dateien (Struts, Spring, EJB etc.) gibt, die den IDEs auch mal durch die Lappen gehen.

    Auto-Completion
    Bei der automatischen Vervollständigung tippt man nur den Beginn eines Wortes ein und die IDE ergänzt die fehlenden Zeichen. Dadurch spart man Tippzeit, reduziert die Wahrscheinlichkeit von Tippfehlern und muss sich nur an den Anfang von Methodennamen erinnern.
    Interessanterweise gibt es auch in programmiersprachen-unabhängigen Editoren wie Emacs oder JEdit Auto-Completion. Dabei werden viel einfachere Verfahren eingesetzt als in den Java-IDEs, die aber erstaunlich gute Ergebnisse liefern. Ein ganz einfacher Ansatz besteht z.B. darin, bei der Auto-Completion nur die Wörter zu berücksichtigen, die in der aktuellen Datei schon mal verwendet wurden. Etwas umfangreicher ist der Ansatz, die Wörter aller Dateien eines Projektes zu verwenden.
    Ähnlich wie beim Cross-Referencing sind diese Auto-Completion-Features der Editoren qualitativ denen der IDEs unterlegen. Aber wie beim Refactoring sie sind schneller.

    Und das ist ein Faktor, den man nicht unterschätzen sollte. Ich ertappe mich beim Java-Programmieren jedenfalls immer wieder dabei, dass ich Namen komplett manuell eintippe, weil ich damit schneller bin als die zäh reagierende Entwicklungsumgebung. Und wenn ich in einem einfachen Editor eine Skriptsprache programmiere, genieße ich es, dass der Editor immer sofort reagiert. Die IDEs sind nur Bruchteile von Sekunden langsamer, aber das reicht schon aus, um den Flow beim Programmieren zu beeinträchtigen.

    August 24, 2006 at 4:16 pm Leave a comment

    Closures in Common Lisp

    Martin Fowler published an online article about closures. He used the Ruby programming language for the examples. There are translations of the examples to Python and a language called Boo.

    As a programming task for myself I did the examples in Common Lisp, which supported Closured already decades ago. A real lisper might be able to write the code in a more elegant way but I think the principle should become clear.

    I didn’t use the Common Lisp Object System (CLOS) since for this simple example an ad-hoc modeling with hashes is simpler.


    ;; Ruby
    ; def managers(emps)
    ; return emps.select {|e| e.isManager}
    ; end

    ;; Common Lisp
    (defun is-manager (emp) (getf emp :is-manager))

    (defun managers (emps)
    (remove-if-not (lambda (emp)
    (when (is-manager emp) emp))
    emps))


    ;; Ruby
    ; def highPaid(emps)
    ; threshold = 150
    ; return emps.select {|e| e.salary > threshold}
    ; end

    ;; Common Lisp
    (defun high-paid (emps)
    (let ((threshold 150))
    (remove-if-not (lambda (emp)
    (when (> (getf emp :salary) threshold) emp))
    emps)))


    ;; Ruby
    ; def paidMore(amount)
    ; return Proc.new {|e| e.salary > amount}
    ; end

    ;; Common Lisp
    (defun paid-more (amount)
    (lambda (emp) (when (> (getf emp :salary) amount) emp)))

    ;; Ruby
    ; highPaid = paidMore(150)
    ; john = Employee.new
    ; john.salary = 200
    ; print highPaid.call(john)

    ;; Common Lisp
    (let ((high-paid (paid-more 150))
    (john '(:name John :is-manager nil :salary 200)))
    (princ (funcall high-paid john)))


    ;; Tests
    (defparameter manager-list
    '((:name Stefan :is-manager nil :salary 150)
    (:name Henning :is-manager t :salary 151)
    (:name Martin :is-manager nil :salary 120)
    (:name Christian :is-manager t :salary 200)))

    (princ #\newline)
    (princ "Test function managers")
    (princ #\newline)
    (princ (managers manager-list))
    (princ #\newline)

    (princ #\newline)
    (princ "Test function high-paid")
    (princ #\newline)
    (princ (high-paid manager-list))
    (princ #\newline)

    August 19, 2006 at 6:01 pm Leave a comment

    Older Posts