Functional programming – Grundlagen
Beschäftigt man sich mit nodeJS und der asynchronen Natur von node apps kommt man ziemlich schnell in Kontakt mit functional style.
Aus meiner Sicht, der lange Zeit hauptsächlich OOP betrieben hat fühlte sich functional erstmal irgendwie komisch an. Zugegeben, OOP in JavaScript fühlt sich auch komisch an.
Macht man sich erstmal von dem Gedanken frei, dass alles irgendwie verdinglicht werden muss ist die Art und Weise wie Daten durch eine Kette von Transformationen geschleust werden ziemlich elegant und kompakt.
Die strikte Trennung von reinen Daten und reinen Funktionen ist vermutlich auch das, was einem OOP-Guy so fremdartig erscheint.
Die Sprachbarriere
Die spezifische Terminologie ist einerseits sehr präzise, andererseits aber auch sehr abstrakt. Da ich sie vermutlich häufiger falsch verwenden würde werde ich versuchen so wenig wie möglich davon einzusetzen.
Damit möchte ich auch vermeiden, dass ein wütender Mob mit den Instrumenten gerechten Zorns vor meiner Haustür erscheint.
Einige Begriffe und Konzepte müssen wir trotzdem vorab klären, fürchte ich…
Exkurs: Arrow Functions
Beispiele werde ich hauptsächlich in ES6 Notation geben. Allein die Arrow-Functions machen den Dialekt im Vergleich dramatisch kompakter.
Es gibt kein this
Im Vergleich zu einer gewöhnlichen Funktion besitzen arrow functions kein this
. Es ist auch nicht möglich einer arrow function mittels fn.apply(obj)
ein this
zuzuweisen.
Sparsam mit Boilerplate
Es gibt eine ganze Reihe von Fällen in denen die üblicherweise erforderlichen Klammern entfallen können.
Bei einer arrow function, die genau einen Parameter besitzt (unär ist) können die runden Klammern entfallen. Ansonsten müssen runde Klammern gesetzt werden.
Bei einer arrow function deren Inhalt ein Ausdruck ist, können die geschweiften Klammern entfallen. Bei dieser Art von arrow function wird der Ausdruck standardmässig zurückgegeben. Es muss also ausserdem kein return erwähnt werden.
const implicitly = who => `Look, ${who}! No return required.`
implicitly("Ma") // Look, Ma! No return required.
Hat man sich einmal an die kompakte Schreibweise gewöhnt liest es sich auch eigentlich sehr flüssig.
Jetzt wo wir das geklärt hätten, kümmern wir uns weiter um die angesprochenen Begriffserklärungen.
First class functions
Bedeutet im Wesentlichen nichts anderes als dass ich eine Funktion wie einen Wert behandeln und an andere Funktionen übergeben kann.
Dieser Umstand macht die meisten der nachfolgenden Konzepte überhaupt erst möglich.
Higher order functions
Eine Funktion höherer Ordnung ist nichts anderes als eine Funktion, die entweder andere Funktionen als Parameter erwartet oder deren Rückgabe eine weitere Funktion ist. Oder beides zugleich.
// function returning a new function
const isDivisibleBy = x => y => 0 === y%x
const isEven = isDivisibleBy(2)
// function taking another function
const res = [1,2,3].filter(isEven) // [2]
In diesem Beispiel sind die Funktionen höherer Ordnung isDivisibleBy
und Array.filter
. Die Funktion isDivisibleBy
vertritt ausserdem noch ein anderes wichtiges Konzept.
Currying
Auch ein Verhältnismässig einfaches Konzept: Eine Funktion die mit weniger Argumenten aufgerufen werden kann als sie insgesamt benötigt.
In diesem Fall erhält man eine neue Funktion, welche die restlichen Argumente aufnimmt.
So ziemlich jede functional library kommt auch mit einer curry
Funktion daher, die es ermöglicht aus beliebigen andere Funktionen eine curried Version zu erzeugen.
Composition
Im Grunde ist das nichts anderes als das Verfahren eine neue Funktion durch Verkettung von mehr oder weniger beliebigen anderen Funktionen erzeugen zu können.
const composeTwo = (f, g) => x => f(g(x))
Diese Variante ist in der Lage genau zwei Funktionen miteinander zu einer neuen verbinden.
const upper = str => str.toUpperCase()
const ask = str => `${str}?`
const askLoudly = composeTwo(ask, upper)
askLoudly("will it blend") // WILL IT BLEND?
// equivalent to
upper(ask("will it blend")) // WILL IT BLEND?
Zu beachten ist hier, dass compose
die übergebenen Daten von rechts nach links gelesen durch die Funktionen schleust.
Es gibt auch die Möglichkeit, Daten von links nach rechts gelesen durch die Funktionen zu schieben. Diese wird üblicherweise pipe
genannt und verhält sich ansonsten gleich.
So ziemlich jede functional library bringt Funktionen zur Komposition in dieser Art mit. Jedoch wird die Komposition mit beliebig vielen Funktionen unterstützt.
Keine Kommentare