Springe zum Inhalt
kurtchen

Unterschiedliche Programmierparadigmen

Empfohlene Beiträge

kurtchen

Kurz vor Beginn meines Informatik-Studiums habe ich hier im Forum gefragt, ob es besser sei, EINE Programmiersprache gut zu lernen oder viele möglichst unterschiedliche Programmiersprachen kennen zu lernen. Das war in diesem Thread.

 

Damals gingen die Antworten der Teilnehmer recht eindeutig in eine Richtung: Lerne zunächst eine Sprache vernünftig.

 

Daran habe ich mich gehalten. Es wurde letztlich auch durch das Curriculum meines Studiums so erzwungen. Java wird bei uns durchgängig als Lehrsprache eingesetzt, was ja wegen der sehr umfangreichen Bibliotheken zu allen möglichen Themen auch gut möglich ist. Ich kenne also verschiedene Aspekte der Programmierung aus der konkreten Perspektive der Programmiersprache Java und aus der allgemeineren Perspektive der objektorientierten Programmierung. Das war rückblickend betrachtet auch gut so. Bei jedem neuen Thema konnte ich meine Vorkenntnisse der Sprache nutzen und mich auf die jeweils neuen inhaltlichen Aspekte konzentrieren, ohne mich mit neuer Syntax rumschlagen zu müssen. Das hätte nur von dem abgelenkt, worum es eigentlich ging. Durch gelegentliche Ausflüge in die Sprachen C# und C++ gewann ich den Eindruck, dass die gleichen Konzepte in einem sprachlich etwas anderen Gewand daherkommen.

 

Ich kann also für mich das Fazit ziehen, dass der damalige Rat der Diskussionsteilnehmer sich als guter Rat erwiesen hat.

 

Damals wurde auch die Meinung vorgebracht, wenn man EINE Programmiersprache vernünftig gelernt habe, sei es viel leichter, andere Programmiersprachen zu lernen. Hier möchte ich aber mittlerweile widersprechen. Der Grund ist die Art und Weise, wie ich meinen Urlaub verbracht habe.

 

Nach dem für mich sehr kraftraubenden Umzug und verschiedenen anderen Gründen habe ich mich dieses Jahr entschieden, die Notwendigkeiten meines Studiums im Sommerurlaub einmal außer acht zu lassen. Mit Informatik beschäftigen wollte ich mich aber schon. Schon seit längeren war ich neugierig, eine LISPoide Sprache zu lernen. Im Modul "Grundlagen der Informatik 4 - Algorithmen und Datenstrukturen" wurde ja ein kleiner Einblick in Clojure gegeben, einen LISP-Dialekt auf der JVM. Leider fand ich den damals zu knapp und oberflächlich, um wirklich zu begreifen, was der Reiz an diesen Sprachen ist. In Gesprächen wurde mir immer wieder ein bestimmtes Lehrbuch genannt: "Strukture and Interpretation of Computer Programms" (kurz SICP) auf der Grundlage des LISP-Dialektes Scheme. Es durchzuarbeiten verändere die Perspektive auf Computer und Programme grundlegend, ganz gleich in welcher Sprache man künftig arbeite. Das ist allerdings eine richtig dicke Schwarte und war für mich als kleines Projekt nebenbei dann doch eine Nummer zu groß.

 

Entdeckt habe ich dann das Skript "Schreibe Dein Programm!" von Herbert Klaeren und Michael Sperber. Es ist ein einführender Programmierkurs auf Grundlage der Sprache Scheme und entstand auf der Grundlage von Erfahrungen aus der Informatik-Grundausbildung der Unis Tübingen, Freiburg und Kiel. Es steht unter der CC-Lizenz und kann unter dieser Adresse runtergeladen werden:

www.deinprogramm.de

 

Mit diesem Buch habe ich jeden Tag ein bisschen beschäftigt. Besonders schön daran sind die zahlreichen Programmieraufgaben, die wirklich bei Adam und Eva anfangen. Ich hatte damit gerechnet, hier sehr schnell voran zu kommen. So war es mir damals gegangen, als ich nach Java einen einführenden Kurs in C++ belegt hatte. Insofern war ich sehr überrascht, wie schwer es mir fiel, Aufgaben in Scheme zu lösen, die mir in Java oder C++ sehr leicht gefallen wären. Die Meinung, neue Programmiersprachen lerne man auf der Grundlage einer bekannten leichter, scheint mir nur unter einer bestimmten Voraussetzung zu stimmen: Wenn man innerhalb des gleichen Programmierparadigmas bleibt. Da heute die meisten produktiv eingesetzten Sprachen objektorientiert sind, kann man leicht den Eindruck gewinnen, alle Programmiersprachen seien im Grunde ähnlich.

 

In LISP kann man in unterschiedlichen Stilen programmieren. "Schreibe Dein Programm!" ist aus der Perspektive der funktionalen Programmierung geschrieben. Der Einstieg war mühsam. Folgende Dinge fielen mir dabei schwer:

- In imperativen und objektorientierten Sprachen, beschäftigen sich Programme damit, Zustände abzufragen und zu verändern. Ein einfaches Beispiel dafür sind Getter und Setter von Klassen. Der Getter fragt den Zustand eines Objektes ab. Der Setter verändert es. In funktionalen Sprachen möchte ich Zustandsveränderungen vermeiden. Datenobjekte bleiben möglichst so, wie sie erstmals angelegt wurden.

- Aus Java bin ich es gewohnt, Variablen fortlaufend neue Werte zuzuweisen, z.B. innerhalb einer Schleife. In Scheme verwendet man statt Schleifen Rekursion. Klar, Rekursion kennt man auch aus Java. Allerdings setzt man sie dort spärlicher ein. In Scheme ist sie überall. Dafür kann Scheme im Gegensatz zu Java Endrekursion erkennen. Der Compiler optimiert den Code so, dass keine neuen Frames auf dem Stack abgelegt werden. In Java führt exzessiver Einsatz von Rekursion schnell zum Stack Overflow, auch wenn man sich eine endrekursive Lösung überlegt hat. Ich habe eigentlich erst jetzt wirklich begriffen, warum im Modul "GdI4 - Algorithmen und Datenstrukturen" so penibel zwischen Endrekursion und anderen Formen der Rekursion unterschieden wurde.

- In Java kann ich bei Methodenaufrufen einfache Typen oder Referenzen auf Objekte übergeben. In Scheme sind Funktionen (Methoden) Daten wie andere Daten auch. Ich kann Funktionen an Variablen binden und/oder als Parameter an Funktionen übergeben. Funktionen können Funktionen als Rückgabewert erzeugen. Das war für mich zunächst ungewohnt und verwirrend.

- Und ja, die vielen Klammern LISPoider Sprachen sind ein Problem. Das ist allerdings ein Problem, dass man durch eine zunächst etwas ungewohnte Art der Code-Einrückung und mit einem guten Editor in den Griff bekommen kann, der das Setzen und vor allem Schließen der Klammern unterstützt.

 

Warum hat es sich gelohnt?

- Die Möglichkeit, Funktionen als Daten zu behandeln, ermöglicht sehr mächtige Abstraktionen. In Java kann ich etwas ähnliches in Form des Strategiemusters machen. Dabei packe ich eine Methode in eine Klasse, die eine Schnittstelle implementiert. In Objekte eingepackt, kann ich Methoden an andere Methoden übergeben. Zum Beispiel kann ich an eine Methode, die eine Liste sortiert, verschiedene Sortieralgorithmen übergeben. Im Rahmen der objektorientierten Programmierung ist so etwas aber relativ umständlich und unübersichtlich. Ich brauche eine Schnittstelle und Klassen, die die Schnittstelle implementieren. In Lisp/Scheme sind Funktionen einfach Daten wie andere Daten auch. Man programmiert Funktionen höherer Ordnung, also Funktionen, die Funktionen verarbeiten oder - noch abgefahrener - Funktionen produzieren. Man kann also sozusagen Methoden-Fabriken programmieren. Das ermöglicht oft viel stärkere Abstraktionen. Man kann ziemlich komplexe Dinge in wenigen Zeilen Code formulieren, der zudem auch leicht verständlich ist, wenn man die Prinzipien einmal verstanden hat.

- Programmiersprachen sind nicht alle ähnlich. Mir ist klar, dass ich Programmierung bislang nur durch die objektorientierte, imperative Brille gesehen habe. Es gibt andere Brillen.

 

Warum würde ich gerne noch mehr über funktionale Programmierung lernen?

- Funktionen als Daten und Higher-Order-Funktionen, die Funktionen verarbeiten oder produzieren, wirken auf mich durchaus aus sehr mächtiges Werkzeug. Damit würde ich gerne umgehen können.

- Programme, die nicht auf Zustandsveränderungen setzen, sollen gut parallelisierbar sein. Bei nebenläufiger Programmierung können ja Race Conditions zu sehr schwer zu findenden Bugs führen. Ich muss immer wieder Codeabschnitte durch Locks schützen, was parallele Ausführung verhindert. Es entstehen Flaschenhälse, was den Nutzen von Mehrkernprozessoren mindert. Funktionale Programme verändern keine Objekte, sie stellen modifizierte Kopien her. Das wirkt auf den ersten Blick ineffizient. Man kann dadurch aber mehr Code parallelisieren und somit Mehrkernprozessoren besser ausnutzen. Heutige und künftige Prozessoren werden vor allem durch mehr Parallelisierung schneller. Die Fähigkeit, zuverlässigen nebenläufigen Code schreiben zu können, sollte also dort wichtiger werden, wo Geschwindigkeit der Datenverarbeitung wichtig ist. Hier haben funktionale Sprachen interessante Ansätze. Leider reicht mein Einblick noch nicht aus, um wirklich zu begreifen, wie das im einzelnen funktioniert.

- Viele objektorientierte Sprachen versuchen zunehmend, funktionale Konzepte zu integrieren. Das macht auch Sinn. Um diese neuen Möglichkeiten wirklich nutzen zu können, muss man aber begriffen haben, was das EIGENE an funktionaler Programmierung ist.

- Die Syntax ist fremdartig aber auch in sich logisch und konsequent. Wenn man sich ein bisschen eingearbeitet hat, findet man den Code tatsächlich gut lesbar, weil die Syntax im Gegensatz zu Java-Code einem durchgängigen Prinzip folgt. Das habe ich schätzen gelernt.

- In LISPoiden Sprachen arbeitet man viel mit der REPL. Das steht für Read Evaluate Print Loop und wirkt ähnlich wie ein Interpreter. Ein eingegebenes Stück Code wird compiliert, ausgewertet, das Ergebnis auf der Konsole gedruckt und das Spiel beginnt von neuen. Man nutzt das für einen sehr interaktiven Programmierstil, bei dem das unmittelbare Testen und ausprobieren von Funktionen eine große Rolle spielt. Das fühlt sich direkter an als die Arbeit in einer IDE. Ich hatte den Eindruck, dass das spontane und unmittelbare Testen dazu führt, dass ich besser "Stein auf Stein" setzen konnte als in Java. Was einmal funktionierte und getestet war, lief zuverlässig. Selten kam es vor, dass ich zurückgehen musste, Fehler in älterem Code ausbessern musste. Bugs gab es eigentlich immer nur in der Funktion, an der ich gerade arbeitete. Es machte Spaß, so zu arbeiten.

 

Was bleibt schwierig an funktionaler Programmierung?

- Im Studium habe ich ja nicht nur eine Programmiersprache und ein Paradigma gelernt. In Kursen wie Softwaretechnik habe ich gelernt, Probleme durch eine objektorientierte Brille zu betrachten. In der objektorientierten Analyse beschäftige ich mich zunächst mal damit, die Realität in eine Klassen und Vererbungsstruktur abzubilden. Die Methoden sind dann den einzelnen Klassen zugeordnet. In der funktionalen Programmierung arbeite ich mit viel allgemeineren Funktionen. Die Datenstrukturen sind auch allgemeiner. Ich müsste den Stoff von Softwaretechnik neu lernen, diesmal aus funktionaler Perspektive. Die Entwurfsmuster, die ich gelernt habe, sind aus objektorientierter Perspektive gedacht. Das führt mich zum nächsten Punkt.

- Es gibt weniger gute Literatur. Man bekommt gute Bücher über funktionale Programmierung. Was fehlt sind Bücher über funktionale Analyse, funktionalen Entwurf, funktionale Entwurfsmuster. Man muss nicht nur auf neue Art programmieren sondern auch auf neue Art denken, was viel schwieriger ist.

- Es ist nicht ganz klar, welche Sprache man lernen soll. Scheme ist eine reine Lehrsprache. Haskell ist sehr streng und darum schwer zugänglich. Scala und Clojure laufen auf der JVM. Man kann Java-Klassen nutzen. Scala versucht objektorientierte und funktionale Programmierung zu verbinden. Clojure ist ein LISP mit Anbindung an das riesige Biotop der Java-Klassenbibliotheken. Beide Sprachen haben sehr interessante Konzepte, um die Probleme nebenläufiger Programmierung zu lösen. Scala bringt von Haus aus sehr mächtige Werkzeuge für die Verarbeitung von XML mit. Diese Sprachen könnten Brücken sein. Es gibt gegenwärtig noch keinen klaren Gewinner, auf den man setzen könnte. Objektorientierte Sprachen integrieren zunehmend funktionale Features. Vielleicht bleibt es auch bei den heute etablierten Sprachen, aber die sind in Zukunft funktionaler als heute. So oder so, das einst akademische Thema könnte sich im Alltag breiter machen.

 

Das entscheidende Problem dürfte bleiben, sich aus der im Studium verinnerlichten objektorientierten Perspektive zu lösen, die einem inzwischen als die "normale" oder sogar "die richtige" Sicht der Dinge vorkommt. Ich habe begriffen aber noch noch lange nicht verinnerlicht, dass man Probleme ganz anders betrachten und strukturieren kann. Wann und wo bringt die funktionale Perspektive Vorteile, so das Code zuverlässiger, nachvollziehbarer oder sogar effizienter wird? Wo ist Objektorientierung die bessere Perspektive? Es gibt noch viel zu lernen.

 

Das war mein Urlaubsprojekt. Spannend war es allemal.

 

Mein Fazit: Ganz zu Beginn meines Studiums hätte es mich vielleicht eher verwirrt, mehrere Paradigmen nebeneinander kennen zu lernen. Es hatte seine Vorzüge, zunächst einmal in der objektorientierten Welt zu leben und dort Stein auf Stein zu setzen. Aber an diesem Punkt meines Studiums finde ich es schon spannend und lohnend zu sehen, dass man "Computation" anders denken kann. Wenn ich die Zeit finde, möchte ich weiter daran arbeiten, meinen Blick zu öffnen.

Diesen Beitrag teilen


Link zum Beitrag
Auf anderen Seiten teilen
Anzeige: (wird für registrierte Benutzer ausgeblendet)

Martin Macke

Sehr schönes Urlaubsprojekt und Zusammenfassung. Ich halte es für eine Fehler, dass im heutigen Curriculum der Informatiker häufig nur ein Paradigma gelehrt wird. Meiner Meinung nach sollte eine imperative, eine funktionale und eine objektorientierte Sprache dazu gehören.

Diesen Beitrag teilen


Link zum Beitrag
Auf anderen Seiten teilen
kurtchen

Noch mal detaillierter zum Vorschlag von Martin Macke: Imperative Programmierung kannte ich schon. Habe ich am Beispiel Pascal gelernt. (Ich meine, mal gehört zu haben, dass die FU Hagen noch immer Pascal als erste Lehrsprache einsetzt.)

 

Bei Springer Campus verwendet man im ersten einführenden Programmierkurs Java als Lehrsprache. Allerdings wird die Objektorientierung dabei zunächst mal ausgeblendet. Man verwendet Java - soweit das möglich ist - als prozedurale und strukturierte Sprache. Erst im zweiten Programmiermodul werden dann die objektorientierten Features draufgesattelt. Ergänzend experimentiert man ein bisschen mit C, erlebt also auch, wie es ist, wenn die Sprache ein bisschen näher an der Maschine ist. Kann man so machen und ich war auch sehr zufrieden mit dem Konzept.

 

Im Modul "Algorithmen und Datenstrukturen" wurde dann - in leider viel zu knapper Form - Clojure vorgestellt. Das war - aus meiner Sicht - aber ungeeignet, um funktionale Programmierung kennen zu lernen. Es ging wohl eher darum, das Thema Rekursion noch einmal zu vertiefen. Java ist dafür leider wenig geeignet. Der Compiler ist z.B. nicht in der Lage, Endrekursion zu erkennen und den Code so zu optimieren, dass der Stapel nicht wächst. In Clojure gibt es ein spezielles Sprachkonstrukt für endrekursive Funktionen. Damit umgeht man diese Beschränkung. Die Syntax ist dann leider nicht mehr ganz so elegant und prägnant wie in LISP.

 

Die Perspektive "funktionale Programmierung" ist mir leider bislang ein bisschen zu kurz gekommen. Liegt vielleicht daran, dass FHs den Anspruch haben, berufsbezogener auszubilden und aktuell Objektorientierung der Hammer ist, mit dem man auf jedes Problem schlägt.

 

Interessant wäre für mich auch noch, Logik-Programmierung am Beispiel von Prolog kennen zu lernen. Aber es gibt leider Grenzen, wie viel ich mir selbst neben Studium und Beruf noch aneignen kann.

 

 

Diesen Beitrag teilen


Link zum Beitrag
Auf anderen Seiten teilen
StefanK

Korrekt, in Hagen wird neben Java als OO Sprache Pascal als Imperative Sprache gelehrt. Außerdem kommt noch Scheme aus funktionaler, und Prolog aus logischer Sicht dazu. Zudem gibt es noch einen Kurs zu domänenspezifischen Sprachen. Die Sprachen, die in anderen Kursen 'nebenbei' verwendet werden (Assembler, ...) lasse ich mal außen vor ;-).

 

Java hat mit Version 8 übrigens einige funktionale Konzepte eingeführt ('Methoden' höherer Ordnung, ...). C# kennt solche Konzepte schon deutlich länger. Das geht sogar soweit, dass man in Java und C# implizit rein funktionale Konzepte wie bspw Monaden findet (wobei man hier schon sehr gut suchen muss).

 

Insgesamt vertrete ich aber auch die Meinung, erst einmal eine Sprache zu lernen. Anschließend kann gerne eine andere im gleichen Paradigma folgen (im Idealfall - aufgrund der Verbreitung - OOP). Danach kann man gerne mal einen Blick über den Tellerrand werfen. Wie du sagst ist für einen sanften Übergang von OOP zu funktional eine Sprache wie Scala geeignet.

 

Übrigens wird eine logische Sprache wie Prolog einen noch größeren 'WTF-Effekt' bei einem OO-Programmierer auslösen als eine funktionale ;-)

Diesen Beitrag teilen


Link zum Beitrag
Auf anderen Seiten teilen
kurtchen
Zitat

Korrekt, in Hagen wird neben Java als OO Sprache Pascal als Imperative Sprache gelehrt. Außerdem kommt noch Scheme aus funktionaler, und Prolog aus logischer Sicht dazu. 

Dann ist dort ja realisiert, was Martin Macke für didaktisch sinnvoll hielte.

 

Die neuen funktionalen Features von Java8 waren in unsere Module zum Teil schon aufgenommen. Das erlaubt an vielen Stellen kompakteren Code. Mein Eindruck ist: Man nutzt das wahrscheinlich nicht so, wie man könnte, weil man gewohnt ist, seine Programme von vorneherein objektorientiert zu konzipieren.

 

Den "WTF-Effekt", den du mir für Prolog ankündigst, behalte ich mal im Hinterkopf.

Diesen Beitrag teilen


Link zum Beitrag
Auf anderen Seiten teilen

Erstelle ein Benutzerkonto oder melde dich an um zu kommentieren

Du musst ein Benutzerkonto haben um einen Kommentar hinterlassen zu können

Benutzerkonto erstellen

Kostenlos ein neues Benutzerkonto erstellen.

Neues Benutzerkonto erstellen

Anmelden

Du hast bereits ein Benutzerkonto? Melde dich hier an.

Jetzt anmelden



×

Wichtige Information

Diese Website verwendet Cookies. Weitere Informationen: Datenschutzerklärung