Vorab einmal, dies ist kein Tutorial, ich werde hier niemandem Haskell beibringen. Ich erstelle diesen Thread um euch die funktionale Programmierung vorzustellen und warum es sich lohnt funktionale Sprachen zu lernen. Ich werde euch hier einige Features funktionaler Sprachen erläutern, die die Funktionale Programmierung so einfach und effizient gestalten. Dies werde ich an Beispielen in der Sprache Haskell erläutern. Für jeden der Haskell lernen will werde ich am Ende ein paar Tutorials oder Bücher auflisten.
Einleitung
Was ist funktionales programmieren?
Die meisten Leute, die mit dem Programmieren anfangen, lernen so genannte imperative Programmiersprachen wie C#, Java, C++ oder ähnliches. Imperative Sprachen verwirklichen das Konzept durch eine Reihe von anweisungen dem Computer zu sagen was er tuen soll, und der Computer führt diesen Code dann Schritt für Schritt aus. Man definiert also wie etwas gemacht wird.
Dem gegenüber stehen die deklarativen Sprachen, zu denen auch die funktionalen Sprachen gehören. In deklarativen Sprachen wird durch Definitionen festgelegt was getan werden soll, und der Compiler entscheidet dann wie es umgesetzt wird.
Die funktionalen Sprachen bauen vollständig auf der Nutzung von Funktionen auf, wobei sich diese deutlich von den Funktionen aus der imperativen Programmierung unterscheiden. Zunächsteinmal die wohl wichtigste Eigenschaft ist, es gibt keine inneren Zustände. Das heißt die Funktionen stehen in keinem Kontext mit anderen Daten, Funktionen sind also frei von Seiteneffekten. Während z.B. in Java jede Methode zu einem Objekt gehört, welches Eigenschaften hat welche die Funktion beeinflussen können, ist dies in der funktionalen Programmierung nicht der Fall. Ein Funktionsaufruf mit dem selben Argument gibt immer das selbe Ergebnis.
Eine andere wichtige Eigenschaft ist, Funktionen sind reguläre Datenobjekte wie Integer oder Doubles, können über Operationen modifiziert werden.
Warum sollte ich Funktionale Programmierung lernen?
Nun es gibt viele Gründe, falls ihr bereits imperatives Programmieren gelernt habt, und von funktionaler Programmierung noch nichts wisst, dann lohnt es sich allein schon um neue Konzepte kennen zu lernen, um bei eurem nächsten Projekt die Optimale Sprache für eure Anforderungen zu wählen.
Aufgrund seiner sehr einfachen und wartbaren Eigenschafften wird Haskell oft in gebieten verwendet in denen schnell Algorithmen entwickelt werden müssen, und deren Korrektheit im Vordergrund steht. So z.B. bei Netzwerkprotokollen, so verwenden riesige Konzerne AT&T oder Ericsson Funktionale Sprachen zur entwicklung der Algorithmen, da sie damit schnell auftretende Probleme beheben können, und dabei keine Fehler aufgrund falscher Implementierung oder Seiteneffekten auftreten. Erricson berichtet sogar davon das seit dem Umstieg auf Funktionale Programmierung (mit Erlang) Zeiteinsparungen von einem Faktor 2 bis 10 gemessen haben.
Für neueinsteiger lohnt sich Haskell vor allem durch seine Einfachheit. Durch das wegfallen der Seitenefekte, und die deklarative Natur, muss man sich kaum gedanken über mögliche Fehlerquellen machen, sondern kann Zielgerichtet drauf los programmieren.
Warum Haskell?
Es gibt viele Funktionale Sprachen, der grund warum ich mich für Haskell entschieden habe ist ziemlich simpel, ich musste Haskell für die Uni lernen, und daher kann ich Haskell einfach am besten. Allerdings gibt es noch einige, nicht so subjektive Gründe für Haskell.
1. Haskell ist nur Funktional. Im gegensatz zu OCaml welches auch OO Eigenschafften hat, kann man sich bei dem lernen von Haskell rein auf die funktionalen Eigenschaften konzentrieren.
2. Open Source. Der GHC (Glasgow Haskell Compiler) als der wichtigste Haskell Compiler ist Open Source, und durch die sehr aktive Community gibt es auch viele Open Source packages.
3. Einfache Syntax. Die Haskell Syntax ist klar strukturiert und dennoch einfach zu lesen.
4. Schnell. Der GHC erzeugt meist sehr effizienten und schnellen Code, dessen Performance sich nicht verstecken muss.
5. Interpreter. Neben dem Compiler gibt es noch den GHCi, einen Interpreter, mit dem man code schnell testen kann.
Weitere Informationen zu Haskell findet ihr auf der Haskell Website:

Funktionale Programmierung
Zunächst einmal sollte man alles Vergessen was man aus der Imperativen Programmierung kennt, funktionale Programmierung geht anders, es ist eine komplett andere Welt mit einer Komplett anderen Herangehensweise.
Die Ausführung eines Haskell Programms besteht aus der der Auswertung von Ausdrücken. So ist 2 + 3 ein gültiger Ausdruck und würde von Haskell zu 5 ausgewertet werden. Bei der Auswertung solcher Ausdrücke greift der Compiler auf die Deklarationen zurück die ihm gegeben wird. in diesem Beispiel wird der Infixoperator (+) verwendet, welcher von Haskell vordefiniert ist.
Um also kompliziertere Ausdrücke zu konstruieren müssen wir wissen wie wir selbst dinge deklarieren.
Beginnen wir zunächst einmal mit den Funktionsdeklarationen, es gibt ein und Nullstellige Funktionen. Nullstellige Funktionen sind dabei Konstanten.
Die Syntax orientiert sich hierbei an der Mathematik. So würde die Mathematische Notation
Code:
drei ∈ ℤ drei = 3
Code:
drei :: Int drei = 3
Einstellige Funktionen, also funktionen die einen Eingabewert erhalten und einen Ausgabewert liefern lassen sich auch sehr ähnlich der mathematischen Syntax definieren.
Die dbl funktion welche das Argument verdoppelt sieht mathematisch so aus
Code:
doppel: ℤ -> ℤ doppel(x) = x + x
Code:
doppel :: Int -> Int doppel x = x + x
Wie ich bereits gesagt habe können Funktionen nur Ein- oder Nullstellig sein, um nun Funktionen zu realisieren welche Mehrstellig sind, also mehr als nur ein Argument entgegen nehmen bietet Haskell die Möglichkeit Kathesische Produkte zu verwenden, so wie es auch in der Mathematik der Fall ist. Als Argument wird dann ein Tupel mit verschiedenen Objekten verwendet statt nur einem Objekt.
Betrachten wir diese sehr einfache Funktion in mathematischer Notation
Code:
plus: ℤxℤ -> ℤ plus(x, y) = x + y
Code:
plus :: (Int, Int) -> Int plus (x, y) = x + y
Code:
plus :: Int -> Int -> Int plus x y = x + y
Obwohl es in Haskell keine mehrstelligen Funktionen gibt ist dieser Vorgang dennoch möglich, durch eine Eigenschaft auf die ich später zurück kommen will, den Funktionen Höherer Ordnung. Hier ist nur wichtig das man mehrstellige Funktionen genau wie Einstellige Funktionen über den Funktionsoperator deklarieren kann.
In der funktionalen Programmierung mit Haskell sind Listen ein wichtiges Kernstück. Listen werden mit der Signatur [Typ] angegeben, z.B. [Int] ist eine Liste von Integer Objekten.
Listen sind (wie sogut wie alles in Haskell) rekursiv definiert. Es gibt die Leere Liste [], sowie den infix Operator : mit dem ein Element zur Liste Hinzugefügt werden kann. x : xs bezeichnet dabei die Liste mit dem ersten Element x, gefolgt von den Elementen der Liste xs. Die Liste [3, 5, 4, 10] ist also ledeglich der Ausdruck 3 : (5 : (4 : (10 : []))). Allerdings aufgrund der kürzeren un übersichtlicheren Schreibweise, verwendet man normalerweise die [Werte] Syntax.
Pattern matching
Betrachten wir die plus Funktion von oben nocheinmal genauer
Code:
plus x y = x + y
Das Muster kann aus verschiedenen dingen bestehen, aus Konstanten (Zahlen, oder Bezeichnern mit die Großbuchstaben anfangen), aus Variablen (Bezeichner die mit Kleinbuchstaben anfangen), Wildcard _ und natürlich kompositionen aus diesen.
Bei dem Einsatz von Variablen wird versucht die Eingabe durch diese Variablen zu substituieren. Unsere Funktion bekommt zwei Integer Objekte, Haskell erkennt nun das Muster enthält zwei Variablen, also wird das erste Argument mit x substituiert und das zweite mit y. Wenn man nun x oder y auf der rechten Seite des = verwendet, so weiß Haskell das damit diese Argumente bezeichnet werden.
Durch die verwendung von Konstanten kann überprüft werden ob das Argument den korrekten Wert hat. Nehmen wir diese einfache Funktion (Hinweis: Bool ist ein Wahrheitstyp und kann die Werte Wahr(True) oder Falsch(False) annehmen)
Code:
istDrei :: Int -> Bool istDrei 3 = True
Wenn man nun istDrei 4 aufrufen würde, würde es zu einem Fehler kommen, da Haskell das Argument 4 nicht umschreiben kann, sodass daraus 3 wird. Um nun auch andere Fälle abzudecken, kann man verschiedene Muster angeben.
Code:
istDrei :: Int -> Bool istDrei 3 = True istDrei _ = False
Patterns können auch komplizierter sein, so kann man z.B. Listen auf die Rekursive : notation überprüfen. Betrachten wir die folgende Funktion
Code:
headIntList :: [Int] -> Int headIntList (x:xs) = x
Das Pattern matching ist ein sehr mächtiges Tool, da man damit die Argumente direkt verarbeiten kann, so wie in dem headIntList Beispiel schon durch das Pattern das erste Element separiert wurde, und gleichzeitig die restliste Berechnet wurde. Zum anderen kann man damit unterscheiden was die Funktion zurückgeben soll, abhängig von der Eingabe. Hier ein weiteres Beispiel
Code:
example :: [Int] -> Int example [] = 0 example x : [] = 3 example _:x:xs = x
Bedingungen
Durch die deklarative Natur Haskells, verwendet man keine aus der imperativen Programmierung bekannten Kontrollstrukturen wie if Abfragen oder Schleifen. If Abfragen können z.B. mit dem Pattern matching gelöst werden. Eine weitere möglichkeit Abfragen durchzuführen ist die Bedingte definition. Betrachten wir folgende Funktion der Mathematik
Code:
maxNum: ℤxℤ -> ℤ maxNum(x, y) = x falls x >= y y sonst
Code:
maxNum :: Int -> Int -> Int maxNum x y | x >= y = x | otherwise = y
Code:
maxNum x y = if x >= < then x else y
Lokale Deklarationen
Neben den Globalen deklarationen können auch lokale, funktionsinterne deklarationen konstruiert werden. Dafür gibt es das Schlüsselwort where, auf das dann verschiedene Deklarationen folgen können
Code:
doppel :: Int -> Int doppel x = y where y = x + x
Rekursion
In der funktionalen Programmierung gibt es keine Schleifen, daher ist Rekursion ein sehr wichtiges Konzept. Wollen wir eine Funtion konstruieren welche uns die Länge einer Liste zurückgibt können wir dies ohne Schleifen nur mit Rekursion und Pattern matching lösen
Code:
lenIntList :: [Int] -> Int lenIntList [] = 0 lenIntList x:xs = 1 + len xs
Code:
len [1, 2] = 1 + len [2] = 1 + 1 + len [] = 1 + 1 + 0 = 2
Neben den vordefinierten Listen ist es allerdings auch möglich selbst Rekursive Typen zu definieren. Die definition eines Binären Integer Baums sieht z.B. so aus
Code:
data BinTree = Leaf Int | Fork (BinTree) (BinTree)
Betrachten wir eine Funktion die mit einem solchen Baum arbeitet
Code:
sumTree :: BinTree -> Int sumTree (Leaf x) = x sumTree (Fork x y) = sumTree x + sumTree y
Higher-Order Funktionen
Ich habe bereits erklärt, es gibt in Haskell keine Mehrstelligen Funktionen, aber mit dem Currying ist es dennoch möglich Mehrstellige Funktionen zu definieren. Um dies zu verstehen betrachten wir doch mal unsere gecurryte plus Funktion:
Code:
plus :: Int -> Int -> Int
Code:
plus :: Int -> (Int -> Int)
Dies ist möglich da Funktionen reguläre Objekte sind. Der Ausdruck plus 2 3 wird also ausgewertet zu (plus 1) 2, wobei plus 1 uns eine Funktion zurückgibt die auf den Eingabewert eins addiert.
Wir können uns damit weitere Funktionen auch definieren
Code:
nachfolger :: Int -> Int nachfolger = plus 1
Wenn eine Funktion andere Funktionen als Argument bzw. Ergebnis verwendet, so bezeichnet man diese als Higher-Order Funktion.
Diese bieten vor allem generische Vorlagen zum konstruieren neuer Funktionen, so wie wir die Funktion nachfolger mithilfe von plus konstruiert haben.
Eine der wohl wichtigsten Funktionen höherer Ordnung ist die map funktion. Diese nimmt eine Funktion und eine Liste entgegen und gibt eine Liste zurück mit den Elementen der Eingabe liste auf die die Funktion angewendet wurde. Wenn man nun map nur eine Funktion übergibt so erhalten wir eine neue Listenmodulierungsfunktion
Code:
incList :: [Int] -> Int incList = map nachfolger
Polymorphes Typsystem
Haskell bietet ein Polymorphes Typsystem, das ist ungefähr patternmatching mit Typen. Man kann in Funktionssignaturen Variablen für Typen angeben, und dann kann die Funktion für beliebige Typen angewendet werden.
Code:
id :: a -> a id x = x
Betrachten wir unsere len Funktion zur berechnung der Länge einer Liste. Diese lässt sich auch Typneutral umformen
Code:
len :: [a] -> Int len [] = 0 len x:xs = 1 + len xs
Wenn man in der Signatur die Typvariable doppelt verwendet, so müssen diese beiden den Selben typ haben, bei id :: a -> a bedeutet das, das die Eingabe und Ausgabe den selben Typen haben müssen, welcher ist aber egal.
Die Signatur von map ist z.B.
Code:
map :: (a -> b) -> [a] -> [b]
Wer sich schonmal mit Mathematik beschäfftigt hat kennt diese Notation
Code:
{ f(x) | x ∈ [1, 10], Bedingungen(x)}
Haskell unterstützt diese notation für Listen
Code:
[f x | x <- [1..10], bedingungen]
Code:
qSort :: [Int] -> [Int] qSort [] = [] qsort (x:xs) = qSort lesser ++ [x] ++ qsort bigger where lesser = [y | y<-xs, y <=x] bigger = [y | y<-xs, y > x]
Unendliche Datenobjekte
Haskell unterstützt unendliche Objekte. Z.B. Eine Liste welche ohne die leere Liste als Ende Konstruiert wird, sondern mit unendlich vielen Zahlen
Code:
inflist = 1 : inflist
Dies funktioniert durch Haskells Lazy Evaluation Auswertungsstrategie. Diese hier ausführlich zu erklären würde viel zu viel Platz und Zeit brauchen, die Idee ist allerdings einfach, Haskell generiert die Liste nur so weit wie es nötig ist.
Um das zu illustrieren betrachten wir die Funktion take
Code:
take :: Int -> [a] -> [a] take 0 _ = [] take y (x:xs) = x : take (y-1) xs
Code:
take 2 inflist =(1) take 2 1:inflist =(2) 1 : (take 1 inflist) =(3) 1 : (take 1 1:inflist) =(4) 1 : (1 : (take 0 inflist)) =(5) 1 : (1 : [])
Schlusswort
So das war mein kleiner Einblick in die Welt von Haskell, und seine unikaten Features. Ich hoffe ich habe einigen eventuell lust auf mehr gemacht.
Falls ihr daran interresiert seit Haskell richtig zu lernen kann ich das Skript meines Professors empfehlen

Oder das Buch: Thinking Functionally With Haskell von Richard Bird (Professor der Oxford University) ISBN: 9781107452640






