Funktionale Programmierung mit Scheme
Scheme (gesprochen: „skiem“) ist eine funktionale Programmiersprache, ein Dialekt von LISP mit Einflüssen von ALGOL. Scheme ist auch eine Sprache, mit der ich mich wegen des Studiums befassen muss und ich finde sie einfach nur grausam.
1. Zwiespältige Betrachtungsweise über die Erlernbarkeit von Scheme
Scheme soll einfach zu erlernen sein. Für einfache Programme ist das sicherlich zutreffend, weil man in Scheme auf einfache Weise Variablen und Funktionen deklarieren kann.
Beispiel für eine Variablendeklaration:
(define x 10) ;x=10
Beispiel für eine Funktionsdeklaration:
(define (f x) ;f(×)=5*x
(* x 5))
So weit ist alles wunderbar und im Prinzip hätte man allein mit so einer Zeile bereits ein Programm erstellt, was auch lauffähig ist. Das Drumherum von imperativen Programmiersprachen kann man sich also sparen. Aber wehe dem, der versucht, größere Programme in Scheme zu schreiben (siehe Punkt 5).
2. Scheme ist typfrei (bzw. benutzt dynamische Typprüfung)
Scheme ist in dieser Hinsicht ähnlich wie PHP. Man kann also alles benutzen und muss sich nicht darum kümmern, ob eine Variable nun ein Integer, ein String, eine Funktion oder sonstwas ist. Der Scheme-Interpreter führt Programme mit unsinnigen Typzuweisungen (z.B. wenn man einen String an eine Funktion übergibt, die einen Integer erwartet) anstandslos aus, bricht aber während des Programmablaufs mit einem Laufzeitfehler ab, sobald die fehlerhafte Stelle erreicht ist.
Typfreie Sprachen sind auf den ersten Blick sehr praktisch (weniger Schreibarbeit, mehr Flexibilität, einfach unkomplizierter), aber das führt eben auch schnell zu Laufzeitfehlern. Eine typstrenge Sprache (z.B. Java) würde bei so einem Unsinn von vornherein die Compilierung und den Programmstart verweigern.
An Scheme (und auch an PHP) lässt sich also monieren, dass Seiteneffekte und fehlerhafte Termbildungen/Zuweisungen/Operationen gefördert werden. Zudem werden größere Programme „unsauber“ und unübersichtlicher, denn am Ende weiß kein Mensch mehr, ob die Variable, die er da gerade benutzt, ein String oder ein Integer ist.
3. Tribut der funktionalen Programmierung
Scheme ist eine funktionale Programmiersprache (mal abgesehen davon, dass es eingeschränkte Möglichkeiten zur imperativen Programmierung gibt). Scheme basiert auf Lisp und Lisp basiert auf dem Lambda-Kalkül und das Lamba-Kalkül sagt aus, dass jeder Term eine Funktion bezeichnet. Die Konsequenz daraus ist, dass man in Scheme praktisch immer nur einen Befehl ausführen kann (eine ganz schöne Umstellung, wenn man nur imperative Programmierung gewohnt ist). Genauso wie bei mathematischen Funktionen darf man zwar mehrere Eingabeparameter übergeben, aber es darf am Ende auch immer nur ein Ergebnis rauskommen. Ich kann z.B. innerhalb einer Funktion nicht zwei Rechenschritte machen, wie z.B.
(define (f x) ;f(×)=(10*(5*x)
(x = (* x 5)) ;FALSCH
(* x 10)
So ist es richtig:
(define (f x) ;f(×)=(10*(5*x)
(* 10 (* x 5)))
Man kann sich mit let und let* behelfen, aber das ist umständlich und nicht direkt mit den Möglichkeiten imperativer Programmiersprachen vergleichbar. Außerdem gibt es ja auch noch rudimentäre Möglichkeiten, imperativ zu programmieren (mit begin), aber das ist böse (weil es eben das funktionale Prinzip von Scheme aufhebt) und daher im Studium eher unerwünscht.
Außerdem sind diese Möglichkeiten alle Syntatic sugar. Syntactic sugar sind Syntaxerweiterungen, also z.B. Befehle, die der Schreibvereinfachung dienen oder wie in Scheme Befehle, die aus imperativen Programmiersprachen übernommen wurden, aber eigentlich nicht „originales Scheme“ sind und sozusagen in Scheme-Befehle konvertiert werden müssen. Es gibt z.B. den while-Befehl in Scheme, um while-Schleifen zu basteln, aber intern macht Scheme daraus eine Rekursion.
4. Rekursionen, Rekursionen, Rekursionen
Aus Punkt 3 folgt auch, dass man häufig rekursive Algorithmen anwenden muss. Das ist nicht unbedingt schlecht, aber der menschliche Geist ist diese Denkweise ungewohnt und daher muss man sich erstmal darauf einstellen. Dinge, die man in imperativen Sprachen 0815 iterativ programmieren könnte, wirken in Scheme daher unnötig verkompliziert.
5. Scheme ist unübersichtlich
Scheme-Programme werden ab einem gewissen Punkt unheimlich unübersichtlich. Also was die Übersicht angeht, könnte selbst Assembler noch dagegen konkurrieren.
Kostprobe:
(define fehlermeldung-keine-liste "Abbruch. Es werden nur Listen als Eingabeparameter akzeptiert.")
(define fehlermeldung-keine-reelle-zahl "Abbruch. Es werden nur Listen mit reellen Zahlen akzeptiert.")
(define add
(lambda (A B)
(cond ((and (list? A) (list? B))
(cond ((and (not (null? A)) (not (null? B)))
(cond ((and (not (pair? (car A))) (not (pair? (car B))) (real? (car A)) (real? (car B)))
(cond ((equal? A '()) B)
((equal? B '()) A)
(else (cons (+ (car A ) (car B)) (add (cdr A) (cdr B)))) ))
(else fehlermeldung-keine-reelle-zahl)))
(else '())))
(else fehlermeldung-keine-liste)))
)
Dieses Programm addiert 2 Vektoren (in diesem Fall Listen) miteinander. Das Programm macht nicht so besonders viel, ist aber nicht auf den ersten Blick zu enträtseln.
6. Klammeritis
Scheme hat das Klammernsetzen zu einer Sportart entwickelt. Das obige Beispiel besteht aus 49 Klammern (bzw. Klammerpaaren) und jede davon muss richtig gesetzt sein, sonst läuft der Mist nicht. Meiner Meinung nach verbergen die Klammern den Blick auf das Wesentliche, denn häufig verwendet man mehr Zeit mit der Suche nach falschen oder fehlenden Klammern, als mit der Realisierung des eigentlichen Programms.
7. Komplexere Programme sind in Scheme kaum zu bewerkstelligen
Selbst 5-Zeiler können wegen der Klammern und der Unübersichtlichkeit von Scheme zur Tortur werden. Die Suche nach Fehlern ist vergleichbar mit der Suche nach der Nadel im Heuhaufen. Wir haben im Studium ein knappes halbes Jahr mit Scheme rumgemacht und sind jetzt an dem Punkt angelangt, wo wir Vektoren miteinander addieren, multiplizieren etc. können. Gegen Ende des Semesters sind wir auch mit Java angefangen und haben praktisch in den ersten Aufgaben gleich die Java-Version unseres Vektorprogramms erstellt. Das, wofür wir in Scheme fast ein halbes Jahr gebraucht haben, haben wir in Java 2 Wochen benötigt. Darüber kann man einmal nachdenken…
Ich frage mich also, wer sich freiwillig antut, ein „richtiges“ Programm in Scheme zu programmieren. Das ist doch sadomachistisch.
8. Was kann Scheme eigentlich, was andere Programmiersprachen nicht können?
Ganz ehrlich: Ich weiß es nicht. Scheme wird häufig wegen seiner Ausdrucksstärke, seiner (angeblichen) Einfachheit und den höheren Funktionen über den grünen Klee gelobt. Wirklich überzeugt bin ich davon aber nicht, denn das alles kann man auch mit imperativen Programmiersprachen realisieren und die Implementierungen sind in Scheme genauso fehleranfällig, wie in anderen Sprachen.
Eigentlich kann man mit Scheme auch nicht so besonders viel machen, außer mathematische Funktionen auszuwerten.
9. Scheme wird interpretiert
Scheme-Programme werden in der Regel von einem Interpreter zum Leben erweckt. Interpreter sind langsamer, als kompilierte Programme. Ergo: Scheme ist nicht mal besonders schnell.
10. Keiner benutzt Scheme
Ok, das ist nicht ganz richtig. Ich habe auf meinem Rechner Scheme-Code in GnuCash, Gimp und vielleicht eine Handvoll kleinerer Bibliotheken entdeckt. Aber auf jeden Fall ist Popularität kein schlagendes Argument, sich Scheme anzutun.
Schlusswort
Der normale Mensch hat in seinem Leben wohl (aus gutem Grund) nur mit imperativen Programmiersprachen zu tun. Scheme ist deshalb ganz interessant, weil man gezwungen wird, auch mal über den Tellerrand zu gucken. Das entschuldigt natürlich nicht, dass Scheme einfach ekelhaft ist und einem die letzten Nerven rauben kann.