Folge19

Functional Programming 1

Dauer: 32 minVeröffentlicht: 27.03.2020

Lambda Kalkül, Functor, Monad - oft wird es erstmal akademisch, wenn man dem Thema "Funktionale Programmierung" das erste mal begegnet und nicht selten schwillt einem dann erstmal der Kopf.
In dieser Ausgabe des Sandpapiers versuchen sich Martin und Theo daran, die Grundlagen von Functional Programming möglichst einfach zu erklären und dabei auf zu viel komplizierten Jargon zu verzichten.

Falls ihr Fragen oder Anregungen habt, immer her damit:

Martin auf Twitter: @felbit

Theo auf Twitter: @on3iro

Sandstorm auf Twitter: @sandstormmedia Oder schreibt uns eine Mail an kontakt@sandstorm.de

Das Sandpapier ist ein wöchentlicher Podcast der Sandstorm Media  GmbH. Wir erzählen aus unserem Alltag, was wir versuchen, anders zu  machen und welchen Herausforderungen und Experimenten wir uns auf  unserem Weg stellen.

Die Folge zum Lesen

Theo
Hallo und herzlich willkommen, moin moin, bei einer neuen Ausgabe des Sandpapiers, unserem Sandstorm Weekly Podcast, bei dem wir über Themen, Herausforderungen und Experimente aus unserem Alltag als Softwareunternehmen sprechen. Heute habe ich mal wieder zu Gast, oder besser gesagt als Co-Host bei mir, den Martin, unseren Schweden-Martin. Hi Martin! Und wir haben gemeinsam beschlossen, dass wir das Thema Functional Programming gerne etwas populärer machen wollen, und deswegen wollen wir heute einfach mal so ein kleines bisschen anfangen über die Grundlagen des Functional Programming zu sprechen und wie sich das so in die Welt der Softwareentwicklung einfügt, also quasi wieder ein etwas technischeres Thema. Und wir versuchen das Ganze mal nicht überakademisch zu machen, sofern es uns dann gelingt, gerade Martin, du bist ja jemand, der durchaus der akademischen Seite rechts zugewandt ist. Genau. Vielleicht, Martin, ja das wird immer noch, das stimmt, das kann gar nicht sein, Martin, vielleicht kannst du mal die Hörerinnen und Hörer so ein kleines bisschen abholen. Also angenommen, wir gehen jetzt mal davon aus, dass die Zuhörer quasi noch gar nichts mit Functional Programming zu tun hatten und in erster Linie, sage ich mal, ganz klassisch Objektorientierung gemacht haben, schließt sich das alles aus, wo ist da der Unterschied, kannst du so einen ganz, ganz kleinen Einstieg geben? Na klar.

Martin
Also da müsste man dann doch ein kleines bisschen akademisch werden, aber ich werfe jetzt keine Formel um mich. Es bleibt Humor an. Nur mal ein bisschen in die Geschichte zurückgehen. Es gibt quasi zwei Stränge der Softwareentwicklung, die sich so in den letzten 100 Jahren aufgetan haben. Also tatsächlich fing es vor, was jetzt etwa 90 Jahre an mit Church, der damals irgendwie den Lambda Kalkus mal aufgeschrieben hat und definiert hat. Und das war also das erste, was man der Programmierung, der modernen Programmierung und der Informatik zuordnet. Und so in diesen knapp 90 Jahren, die wir jetzt haben, haben sich zwei Stränge entwickelt. Und das eine ist halt dieses imperative, sequenzielle Programmieren, das ich denke die meisten von uns auch auf einer täglichen Basis machen. Wo halt dann auch die Objektorientierung mit reingehört, wobei das nicht ganz einfach so zuzuordnen ist, aber wir lassen das jetzt erstmal so. Und auf der anderen Seite halt die deklarative Programmierung bzw. ich sag mal so das, was man im Allgemeinen eher so ein bisschen als mathematischen Zweig der Programmierung ansieht, wo unter anderem die funktionale Programmierung wieder zugehört. Genau, hat das die Frage schon beantwortet? Ich bin glaube ich gerade ein bisschen im Kopf abgestiegen.

Theo
Naja, okay. Wir wissen jetzt zumindest, dass die funktionale Programmierung offensichtlich ein kleines bisschen anders funktioniert und dass sie irgendwie mathematischer ist. Wenn ich jetzt funktionale Programmierung höre, wäre mein naiver Gedanke, naja, okay, dann mach ich halt gar keine Klassen mehr und stattdessen schreib ich irgendwie Funktionen hin. Und das war's dann. Und dann ist das funktionale Programmierung. Stimmt das? Genau. Dann machen wir noch mehr dazu.

Martin
Genau, das stimmt so, das kannst du so lassen und wir machen jetzt Feierabend, oder?

Theo
Dann ist jetzt vorbei. Und also meine Funktionen, die baue ich dann beispielsweise so auf, ich nehme einfach meine sequenziellen Statements, pack die in die Funktion rein und dann mache ich da meine Datenbank-Aufrufe und mache einfach alles irgendwie und zerstückele das einfach nur ein bisschen und teile das in Funktionen auf, damit es schöner aussieht. Das war's dann richtig.

Martin
Ich finde, du erklärst das richtig gut, das Konzept. Und das ist, nee, das ist also, ja, ich weiß, du meinst, das ist lustig, aber das ist leider genau das, was ich, was ich sehr häufig sehe. Also wenn wir also Funktionale Programmierung ist grundsätzlich halt nicht einfach nur dein Code in Funktionen und Modulen zu schreiben, anstatt in Klassen und Methoden, sondern es geht halt wirklich darum, dass du Funktionen als Datentyp begreifst und mit diesem Datentyp Funktionen arbeitest, als, ich sag mal, sogar primitiver Datentyp, im Sinne von auf einer Ebene mit Integern und deinem Boolean Value ist die Funktion als solche. Und wenn du das akzeptierst oder wenn das deine Sprache ermöglicht, so zu arbeiten, dann kannst du Funktionale Programmieren. Denn Funktionale Programmieren bringt halt so ein ganzes Set an, ich sag mal, eigenen Regeln und eigenen Strukturen und eigenen Konzepten mit sich. Aber die erste und wichtigste Eigenschaft ist, dass die Funktion selbst nicht anderes als ein Datentyp ist bzw. dass Funktionen nicht anders als Daten sind.

Theo
Also, sprich, Funktionen sind Werte, die man in seinem Programm hin und her reichen kann. Mit anderen Worten, wenn ich dich richtig verstehe, jede Sprache, die es unterstützt, dass ich beispielsweise Funktionen als Rückgabewerte von Funktionen haben kann oder eben auch Funktionen als Parameter an andere Funktionen übergeben kann, sollte prinzipiell erstmal grundsätzliche Formen von funktionaler Programmierung ermöglichen, korrekt?

Martin
Also was du sagst ist korrekt, ich stoße mich nur an den Wort Wert, denn in der Funktionalprogrammierung hat das Wort Wert eine ganz eigene Bedeutung und zwar ist der Wert nämlich das Ergebnis einer Funktion. Also wenn ich eine Funktion, die hat einen Parameter und wenn ich dort was reingebe und das, was dann da rauskommt am Ende, das ist der Wert einer Funktion. Das heißt, wenn du sagst, dass eine Funktion quasi ein Wert ist, dann ist das nur halb korrekt, weil eine Funktion ist gemeinsam mit ihrem Parameter ein Wert.

Theo
Okay, verstehe. Das war ausreichend akademisch. Danke. Entschuldigung. Und jetzt habe ich ja vorhin bewusst so ein kleines bisschen ketzerisch schon angedeutet, dass es nicht einfach nur darum geht, wahrscheinlich sequenziell in Funktionen etwas runterzuschreiben, sondern dass es darüber hinaus noch so ein paar, ich sag mal, Rahmenbedingungen gibt, die in der funktionalen Programmierung oft eine große Rolle spielen. Mir schwebt da gerade so als Stichwort das Thema Pure Functions vor, also pure Funktionen. Magst du vielleicht ganz kurz erläutern, was es damit auf sich hat und warum das gut ist und warum das vielleicht auch gut sein kann, selbst wenn man gar nicht pur funktional programmieren möchte, sondern beispielsweise in einem imperativen oder objektorientierten Kontext unterwegs ist? Ja.

Martin
Tatsächlich kommt der Begriff Pure Function aus dem nicht funktionalen Programmierungen. Weil man in der Funktionalen Programmierung gar keine Unterscheidung machen kann zwischen Pure Functions oder anderen Funktionen. Jetzt fängt der Until grad an zu schnarchen. Ich hoffe, das ist nicht zu laut. Also was ist eine Pure Function? Pure Function ist tatsächlich einfach, jetzt wird es vielleicht doch noch ein kleines bisschen mehr akademisch. Ich stelle das mal so hin, ich erkläre es später. Es ist tatsächlich ein reiner Morphismus. Das heißt, eine Pure Function nimmt Argumente entgegen und gibt Ergebnisse einer Berechnung zurück, ohne dabei in irgendeiner Art und Weise auf die Umgebung zu wirken. Das heißt, ohne einen Seiteneffekt zu erzielen. Das heißt, eine Pure Function kann am globalen State nichts ändern, außer mit ihrem Ergebnis.

Theo
Okay, versuchen wir es mal auf die praktische Ebene runterzubrechen. Kannst du mal ein Beispiel für eine Pure Function geben und dann vielleicht einen Beispiel für eine Funktion, die in irgendeiner Form einen Seiteneffekt oder den Seiteneffekt selbst erklärt?

Martin
Genau, also eine Pure Function wäre zum Beispiel, wenn ich sage, ich habe eine Funktion, die heißt Sum, S-U-M, und was die tut, ist, sie kriegt eine Liste rein als Argument und gibt die Summe aller, also eine Liste von Integers beispielsweise, und gibt die Summe aller dieser Werte, die in dieser Liste drin sind, zurück. Wenn sie tatsächlich nichts anderes tut, als diese Liste einzulesen, die Integers miteinander zu addieren und am Ende das Ergebnis als Return Value zurückzugeben, dann haben wir hier eine Pure Function. Eine nicht Pure Function wäre es, wenn sie zum Beispiel dieses Ergebnis irgendwo hinloggt, also tatsächlich sagt, ich schreibe das in einen Pfeil, oder ich habe zum Beispiel, ich kriege die Liste nicht reingegeben als Parameter, sondern ich habe den, ich habe den, was weiß ich, ich rufe diese Funktion auf und die macht dann erstmal eine Kommandozahlenabfrage, gibt mir mal bitte Werte, die ich da berechnen kann, und das sind alles Seiteneffekte, das heißt, da greift die Funktion ja auf Ressourcen zu, die nicht der Funktionen inne liegen, also zum Beispiel halt auf Input, Output oder dergleichen. Und dann ist es keine Pure Function mehr, dann haben wir einen Seiteneffekt.

Theo
Das heißt, wenn ich dich richtig verstehe, kann man mit funktionaler Programmierung eigentlich nichts machen oder zumindest mit pur funktionaler Programmierung, weil ich ja keine Seiteneffekte habe und meine Programme eigentlich nichts tun.

Martin
Naja, das ist nicht ganz korrekt. Das ist das, was man lustigerweise immer über funktionale Programmierung sagt. Aber korrekt ist das nicht. Mit rein funktionale Programmierung kann man auch jede Menge tun. Zumindest, also zumindest die Berechnungen, die angestellt werden müssen, kann man alle voll funktional machen. Natürlich müssen die Daten irgendwo herkommen. Und an der Stelle hast du immer irgendeine Art von Interface, was dann auch Seiteneffekte benötigt. Deswegen ist es bei funktionalen Sprachen, die also sehr, sehr stark diese funktionale Paradigma erfahren, häufig so, dass du dass du sagst, du hast einen funktionalen Kern und der ist möglichst groß, also einen pure funktionalen Kern, der ist möglichst groß und hast dann quasi ein Layer am Rand, ein nach außen hin sozusagen sichtbaren Port, wo du dann Daten rein und raus kriegst. Das könnte zum Beispiel ein IOPort sein, wo du also sagst, ich habe hier zum Beispiel eine Datenbank, aus der kann ich lesen und ich habe einen Bildschirm, auf dem kann ich die Daten, die fertig berechneten Daten anzeigen oder ich schreibe das in einen Pfeil oder sonst irgendwas. Das ist ja meistens IOU tatsächlich. Genau und das ist so, also das Konzept ist tatsächlich, dass du versuchst, so viel deines Programms wie möglich in dem Sinne pure zu halten und dann nur einen ganz, ganz, ganz dünnen Layer im Pure, also mit Seiteneffekten.

Theo
So dass ich quasi, wenn ich mich in den Programm bewege und ich weiß, das ist eine sehr funktionale Codebase, dann kann ich relativ genau pinpointen, wo diese Seiteneffekte passieren. Das heißt, es gibt da relativ wenig Fehlerquellen, die irgendwo tiefer im Code stecken könnten, die jetzt irgendetwas tun, was nicht klar nachvollziehbar ist. Und auf der anderen Seite...

Martin
Der wichtige Punkt ist eigentlich, also wann tritt der Fehler auf? Und wenn du eine möglichst pure und vielleicht sogar typgetriebene Entwicklung deines deiner Software hast, dann treten die Fehler halt zum Compile Zeitpunkt auf. Das heißt, der Compiler wird dir dein Programm um die Ohren hauen, wenn er nicht wenn es quasi nicht in der Lage ist, das, was du da hingeschrieben hast, aufzulösen. Bei einer stark funktionalen Sprache, wie wir haben jetzt in den letzten Wochen viel über Haskell gesprochen. Bei Haskell ist es zum Beispiel so, dass der Compiler tatsächlich dein Code komplett inlined, also daraus quasi ein dickes, fettes Statement macht. Und wenn er das nicht übersetzt kriegt, dann haut es dir um die Ohren. Und das heißt, dass du die meisten Fehler tatsächlich zur Compile Zeit findest. Und die einzigen Fehler, die noch zur Runtime auftreten können, sind IO-Fehler. Und das ist halt sehr, sehr schön. Das ist das, wo dann Elm zum Beispiel das Statement macht, No Runtime Errors, was natürlich wohl schön ist. Du hast immer irgendwelche Möglichkeiten, vor allem, wenn du irgendwo im JS Interop bist. Aber grundsätzlich ist die Idee tatsächlich, dass deine Business Logik, das, was tatsächlich anstrengend ist, zu entwickeln, da wo ganz viel Hirnschweiz rein muss und das, was auch den Kunden am Ende richtig Geld kostet, das soll quasi so stabil und so fehlerfrei wie möglich sein. Und da ist funktionale Programmierung auf jeden Fall etwas, was dir helfen kann.

Theo
das ergibt Sinn. So, jetzt haben wir die Funktionen. Wir haben pure Funktionen, die wir hin und her erreichen können. Und diese Funktionen transformieren, zumindest im Kern, da wo keine Seiteneffekte sind, Daten von einem Typen in einen anderen Typen. Genau. Korrigiere mich, wenn ich irgendetwas sage, was nicht stimmt. Nein, ich meine.

Martin
Na ja, bis dahin, bis zum nächsten Mal.

Theo
Und in diesem Kontext, damit es eben seiteneffektfrei bleibt, gibt es ein weiteres Konzept namens Immutability. Magst du da vielleicht mal kurz zu sagen, warum Immutability so wichtig ist und warum wir das, also nach meinem Gefühl, auch in nahezu jeder anderen Sprache, in sehr, sehr vielen Punkten tatsächlich inzwischen nutzen und ganz, ganz wenig nur noch mutable arbeiten?

Martin
Ja, wir können ja mal ganz kurz, also ich würde jetzt mal sagen, was ist das eigentlich für die, die das vielleicht noch nicht gehört haben. Und danach würde ich die Frage gerne an dich zurückgeben, weil du hattest beim Workshop, den wir hatten, so ein schönes Beispiel dafür. Vielleicht kannst du das dann noch mal rauskramen, solange ich erkläre, was das eigentlich ist. Genau, bei Immutability geht es darum, dass wir ausschließlich mit unveränderlichen, konstanten Arbeiten. Das heißt, wenn ich einen Parameter hinein bekomme in eine Funktion, dann wandel ich diesen Parameter nicht. Das heißt, da gibt es ja immer dieses Copy-by-Reference. Das heißt, ich würde in einer Funktion tatsächlich auf einem Objekt arbeiten und dieses Objekt verändern. Das wäre an sich schon Seiteneffekt, die Funktion. In Funktionale Programmierung ist es aber so, dass wir das gar nicht können, denn tatsächlich ist alles immutable. Das bedeutet, dass wir, wenn wir einmal in einer Funktion eine Variable, was keine Variable ist, sondern konstant einen Wert gegeben haben, dann können wir diesen nicht mehr verändern. Es gibt dann noch die Möglichkeit auf Re-Assignment, das bieten einige Sprachen, dass ihr sagen könnt, okay, ich habe jetzt weiter oben, steht halt mein Integer x und das ist halt 5 und weiter unten schreibe ich dann Integer x und das ist 6. Das wäre ja auch eine Form von Mutability. Also das nennt man halt Re-Assignment. Auch das geht in vielen pure funktionalen Sprachen nicht. Das hat vor allem was mit der Nähe zur Mathematik zu tun, denn das kann man sich jetzt ganz einfach vorstellen, ich schreibe mir irgendwie eine mathematische Funktion hier hin und berechne die nach und nach und nach und redefiniere dann irgendeine meiner Parameter, die war halt vorher x5 und jetzt ist das x aber hat einen anderen, es ist dieselbe Bezeichnung, hat aber einen anderen Wert dahinter liegen. Da würde jeder Mathelehrer dahin nehmen, einen fetten roten Strich machen und dir das Ding um die Ohren hauen. Weil verwirrender geht es ja gar nicht. Und genau das ist tatsächlich der Hintergrund, dass du also mit Daten arbeiten kannst und du kannst dich darauf verlassen, dass diese Daten nicht von einem Dritten manipuliert werden. Das heißt auch, dass wenn du Daten irgendwo im Speicher abgelegt hast, diese Daten nicht von jemand anderes verändert werden. Es kann sein, dass die von jemand anders gelesen werden, aber es kann nicht sein, dass die von jemand anders verändert werden. Denn Daten dürfen überhaupt gar nicht mehr verändert werden. Es kann sein, dass die von jemand anders gelesen werden.

Theo
ein Beispiel. Genau, vielleicht ein ganz gutes Beispiel, was ich ganz gerne im Rahmen von unseren React-Schulungen bringe, ist tatsächlich in React, was ja quasi JavaScript bzw. TypeScript ist, gibt es die wunderbaren Array-Methoden, die sicherlich viele Hörerinnen und Hörer, die zumindest in dem Ökosystem arbeiten, kennen, also sprich Map Filter Reduce und die arbeiten alle Immutable. Das bedeutet, wenn ich auf einer Liste oder bzw. einem Array in JavaScript Map ausführe, dann bekomme ich ein komplett neues Array, also quasi die alte Referenz bleibt komplett so erhalten, wie sie war und ich habe eine neue Referenz, ein neues Array, mit dem ich dann weiter arbeite. Nun ist es aber so, dass in JavaScript nicht alle dieser Funktionen so funktionieren und so ein ganz typischer Fallstrick ist, beispielsweise Array.sort, also die Funktionen, sozusagen die Elemente eines Arrays sortiert werden. Die ist tatsächlich ArbeitetMutable und wenn man Array.sort auf einer Liste aufruft, dann wird das ursprüngliche Array verändert und gleichzeitig aber auch zurückgegeben, was noch viel mehr dafür sorgt, dass man denken könnte, es sei ein neues Array, wenn man es nicht weiß. Und das Beispiel, was ich in den Schulungen gerne bringe, ist, dass ich eine arbiträre Funktion nehme, die die Werte irgendwie sortiert und mit der Array.sort-Funktion, die in ein neues Array speichert oder zumindest in einer neuen Variable zuweist und dann habe ich sozusagen meine Eingangsliste und die sortierte Liste und prüfe dann, ob sozusagen das erste Element der Liste identisch ist. Natürlich sind die Listen so konstruiert, dass sie, sage ich mal, das erste Element im Ausgangszustand, nicht das erste Element im Ergebniszustand sein wird. Und wenn ich das mutable mache, dann wird immer true bei einer solchen Funktion rauskommen, weil ja die Referenz identisch ist, also mein Eingangs-Array ist mein Ergebnis-Array. Dann als zweiten Schritt schreibe ich eine Funktion hin, ohne dass wir den Code in irgendeiner Form ausfüllen, die das Ganze im mutable macht. Da gibt es verschiedene Möglichkeiten in JavaScript, da möchte ich jetzt gar nicht zu detailliert darauf eingehen. Und in diesem Fall kläre ich dann mit den Schulungsteilnehmern sozusagen den Erwartungszustand, dass ja in dieser Variante das Ganze false sein muss. Und rein auf der gedanklichen Ebene gehen dann prinzipiell meistens erst mal alle mit. Dann klärt man so ein, zwei, drei Fragen und dann gehe ich einfach in eine Browser-Konsole und mache ein Beispiel, wo ich beide Funktionen definiere und nacheinander jeweils in einem Console-Log aufrufe. Und ich mache aber eine fiese Sache und zwar schreibe ich meine Liste vorher in eine Variable und rufe beide Funktionen mit der gleichen Variable auf und die Sort- Funktion rufe ich als erstes auf. Und was dann passiert ist, dass sozusagen in meiner ersten Funktion die Liste bereits sortiert wird und dann kommt sie sortiert in die immutable Funktion rein und dann sind plötzlich beide Ergebnisse true. Ich hoffe, das kann man einigermaßen nachvollziehen, ist glaube ich in einem Podcast tatsächlich ein bisschen abstrakt. Und das fiese ist sozusagen, dass das so auf den ersten Blick nicht auffällig ist. Meistens sitzen die Teilnehmerinnen und Teilnehmer bei einer Schulung dann erst mal so kurz da und denken so, was ist da denn jetzt passiert? Und dann dämmert es meistens den ersten schon und sagt, Moment mal, das ist ja quasi, sind nicht zwei unterschiedliche Arrays und und so weiter und so fort. Und das ist, finde ich insofern ein gutes Beispiel, weil es zeigt, wie subtil Mutability tatsächlich für fiese kleine Bugs sorgen kann. Und witzigerweise ist es tatsächlich auch so, dass dieses Beispiel habe ich mir nicht so mal eben an den Fingern herbeigezogen, sondern ich habe quasi genau die Struktur, wie ich es gerade beschrieben habe, so habe ich das Beispiel entwickelt und bin selbst über den Fehler gestolpert und habe für so einen Bruchteil eine Sekunde gedacht, hä, wieso kommt jetzt hier true raus? Und da habe ich mich wahnsinnig darüber gefreut, weil es quasi das perfekte Beispiel ist, wie sich so kleine Fehler einschleiden können. Genau, das ist das Beispiel.

Martin
Tatsächlich ein gutes Beispiel. Ich glaube, dass vor dieser Episode hätten wir sowieso ganz am Anfang mal sagen sollten, legt euch den Zettel und den Stift mit zurecht und schreibt ein bisschen mit. Weil es gibt so Episoden, bei denen kann man vielleicht nicht alles im Kopf mitgehen. Es waren hier Episoden, die gefiel mir aber ganz gut.

Theo
Ja, man kann es ja langsamer hören und nochmal, weil das schöne bei einem Podcast ist, den kann man ja immer wieder anhören. Von daher, und dann kann man sich automatische Transkripte generieren lassen oder so. Genau. Wir sollten mal einen Podcast über das Podcast machen.

Martin
Genau. Genau.

Theo
Das ist Meter, Meter, Meter. Ich glaube, jetzt hängen wir die Leute ab. Okay, so. Martin, wir haben jetzt schon einige wichtige Bestandteile. Ich überlege gerade, du hattest vorhin schon mal das Thema Morphismus angedeutet. Wir werden, glaube ich, im Rahmen dieser Folge das Thema Functional Programming Basics bei Weitem nicht erschöpfen. Das heißt, wir werden mit Sicherheit eine Folge machen, eine Anschlussfolge. Aber ich glaube, das Thema Morphismus, da so ganz grob noch mal drauf einzugehen, ohne den Hörern Angst zu machen, weil es schon wieder furchtbar mathematisch klingt, ist, glaube ich, ganz sinnvoll, weil sehr, sehr viel in der wirklich theoretischen Funktionalen Programmierung darauf basiert. Magst du zwei, drei Worte dazu sagen oder vielleicht auch mehr? Ja, okay.

Martin
Martin, wir haben jetzt schon einige wichtige Bestandteile. Ich überlege gerade, du hattest vorhin schon mal das Thema Morphismus angedeutet. Wir werden glaube ich im Rahmen dieser Folge das Thema Functional Programming Basics bei weitem nicht erschöpfen. Das heißt, wir werden mit Sicherheit eine Folge machen, eine Anschlussfolge. Aber ich glaube, das Thema Morphismus da so ganz grob noch mal darauf einzugehen, ohne den Hörern Angst zu machen, weil es schon wieder furchtbar mathematisch klingt, ist glaube ich ganz sinnvoll, weil sehr sehr viel in der wirklich so theoretischen Funktionalen Programmierung darauf basiert. Magst du zwei drei Worte dazu sagen? Oder vielleicht auch mehr? Ja, ob ich das in zwei drei Worten hinkriege. Aber ich halte mich, also doch, warte mal, das kriege ich hin.

Theo
Das ist keine Challenge, Martin. Idealerweise formulierst du es so, dass es verständlich ist.

Martin
Ach so. Aber also die kürzeste Erklärung ist, alle Pure Functions sind Morphismen. Ach so. Na gut. Genau. Das ist quasi die Aussage, die ich eben getroffen habe, dass alle. Morphismen, Pure Functions sind also das egal. Tatsächlich ist es halt so, ein Morphismus ist nichts anderes als. Etwas an Gebilde, das auf der einen Seite etwas rein nimmt und auf der anderen Seite etwas anderes rausgibt und das deterministisch. Also wenn ich auf der einen Seite Integerwerte rausnimmt oder sagen wir mal, ich gebe auf der einen Seite Strings rein, auf der anderen Seite kommen Integers raus. Zum Beispiel die Funktion Length ist hier immer so ein schönes Beispiel. Length ist ein Morphismus, denn Length nimmt auf der einen Seite eine Liste von Strings rein. Und gibt auf der anderen Seite oder erstmal nur ein String rein und gibt oder eine Liste und gibt auf der anderen Seite eine Länge dieses Strings oder diese Liste raus. So und das kann ich jetzt auch schön konkurriert oder zusammenführen mit einer Funktion, die ich da außen rum baue, nämlich noch eine Funktion, die sagt, pass auf, ich nehme auch Listen von Listen oder Listen von Strings und gebe eine Liste von Integers zurück. Auch das ist ein Morphismus. Also alles, was quasi ein Parameter rein nimmt und daraus etwas anderes generiert und das deterministisch, das ist ein Morphismus. Und das ist die Grundlage von funktionaler Programmierung. All das ist alles, was wir funktionaler Programmierung hinschreiben in irgendeiner Art und Weise Morphismus. Denn was kein Morphismus ist, ist sinnlos hinzuschreiben.

Theo
Okay, wie meinst du das? Na ja, was? Also warum ist das denn so?

Martin
sinnlos? Naja, was ist kein Morphismus? Also ich kann in meinem Programm irgendwo eine 5 schreiben. Ohne damit irgendwas zu machen, aber das ist sinnlos. Also da passiert nichts. Das heißt, und sobald ich diese 5 an einen Wert binde, ist der Wert eine Funktion, der diese 5 zurückgibt. Und schon habe ich wieder Morphismus. Also deswegen ist quasi das die Grundlage von funktionalen Programmierung.

Theo
Und wenn ich jetzt aber eine Funktion hätte, die nichts anderes tut, als irgendwie einen Seiteneffekt auszulösen, indem sie beispielsweise etwas an den Bildschirm ausgibt oder in eine Datenbank schreibt, dann ist das...

Martin
Dann sind wir auch nicht mehr in der funktionalen Programmierung, sondern da sind wir wieder in der sequenziellen Programmierung.

Theo
Es gibt verschiedene Typen von Morphismen. Ich glaube, das ist aber schon ein anderes Thema, wenn wir uns da mal etwas detaillierter mit auseinandersetzen. Deswegen, ich würde mal noch so teasen, was in einer späteren Folge vielleicht kommt, und dann habe ich noch eine abschließende Frage für diese Folge. Wir werden uns in einer oder mehreren nächsten Functional Programming-Folgen auf jeden Fall mit dem Thema Curing und Partial Application ein bisschen beschäftigen. Und es geht nicht ums Kochen, so viel sei gesagt, aber es geht um ein sehr spannendes Thema. Und auch das Thema Rekursion und eben auch die Morphismen werden auf jeden Fall noch mal eine Rolle spielen. Die Frage, die sich mir jetzt gerade noch stellt, wir haben jetzt ungefähr geklärt, wie sieht so ganz grob pure funktionale Programmierung aus. Was ist denn jetzt aber, wenn ich in einer Sprache wie Java oder eben auch JavaScript unterwegs bin? Also wir wissen in JavaScript beispielsweise, ich kann Funktionen hin und her reichen, und das scheint ja irgendwie wichtig zu sein, hatten wir so eingangs angedeutet. Aber gibt es aus seiner Sicht gute Möglichkeiten, so bestimmte Dinge der funktionalen Programmierung auch in andere Programmierstile mit einfließen zu lassen und davon zu profitieren?

Martin
Du hast es ja selbst gesagt. Gerade Pure- und Total Functions sind so was, das auch in anderen Sprachen total viel Sinn ergibt. Man sieht es auch immer häufiger. Was gerade sehr stark im Vormarsch ist in den letzten Jahren, auch in den sequenziellen Sprachen und den objektorientierten Sprachen, sind Lambdas, die man inzwischen überall findet. Das heißt, dass ich nicht mehr tatsächlich irgendwo ein Objekt habe, was mir eine Methode gibt, dass ich irgendwas sortieren kann, oder sagen wir mal einen Filter, eine Filterfunktion, der ich einfach in Lambda mitgeben kann und sagen kann, hier ist die Funktion, die du bitte auf jedem Element meiner Liste ausführst und wenn da Tool zurückkommt, dann will ich das Element haben. Das ist etwas, das ist neu, ich denke, neu in der objektorientierten Welt. Ich denke, das ist bei, ich glaube, Lambdas sind bei Java 8 oder so was mit reingekommen. Das hat also eine ganze Weile gedauert. Und das sieht man jetzt aber sehr, sehr viel häufiger und es ist auch eine gute Sache. Also gerade Lambdas sind halt ein sehr, sehr mächtiges Werkzeug. Und das ist ja das, genau das, was tatsächlich auch der erste Schritt Richtung funktionale Programmierung, den jeder geht und den jeder auch, ich glaube, relativ einfach nachvollziehen kann, ist, dass ich hier tatsächlich eine Funktion als Parameter in eine andere Funktion übergebe und damit einfach als Datum, als einfache Daten mit in diese Funktion reingebe und mit denen ich dann weiterarbeiten kann.

Theo
Also das vielleicht nochmal für die Zuhörerinnen und Zuhörer, Lambda ist quasi nichts anderes als eine anonyme, also nicht vorher definierte benannte Funktion, die direkt in einem anderen Funktionsaufruf übergeben wird, richtig? Richtig, genau. Genau. Darüber hinaus, vielleicht sollten wir noch ganz kurz klären, was bringen uns denn so Pure Functions, beispielsweise auch in der objektorientierten Programmierung? Also wir schweben beispielsweise Sachen vor eine Pure Function, lässt sich per Definition sehr sehr gut unit testen, weil sie nur über ihren Input und ihren Output definiert ist, das ist so ein großer Vorteil. Martin, vielleicht finden die ja aus dem Stegreifen noch so zwei, drei andere Dinge eigentlich? Na, das erste Mal.

Martin
Weil sie nur über ihren Input und ihren Output definiert ist. Das ist so ein großer Vorteil. Martin, vielleicht findet ihr aus dem Stegreifen noch so zwei, drei andere Dinge? Das erste, was mir immer einfällt, ist Composability. Das heißt, du kannst halt Pure Functions, wo du deterministisch genau weißt, was vorne reinkommt und hinten rausgeht, Unit Test Bar quasi nachvollziehen kannst. Die kannst du natürlich auch schön miteinander verbinden und kannst daraus also eine Funktion, die quasi das beste Beispiel, was man immer wieder jetzt, was totgequatscht ist, ist MapReduce. MapFilterReduce, das ist so eine Dreierkombi, die man immer wieder sieht. Das ist so das Standardbeispiel für Functional Composition. Das heißt, du hast eine erste Funktion, die nimmt eine Liste und gibt dir eine Liste gleicher Länge zurück, allerdings mit eventuell veränderten Daten. Die zweite Liste nimmt eine Liste rein und gibt dir eine eventuell verkürzte Liste zurück gleichen Datentyps. Und die dritte nimmt eine Liste rein und gibt dir ein Ergebnis, also ein Datum zurück. Also nicht eine Liste, sondern ein Datum, was in irgendeiner Art und Weise durch diese Liste beeinflusst ist. Die kannst du aber schön hintereinander hängen, indem du sagst, okay, pass auf, ich nehme zuerst meine Liste entgegen, sagen wir mal die Namen von allen Centstormern. Und dann filter ich alle jenen raus, deren Namen mit M beginnt. Das heißt, alle, die nicht mit M beginnen, bleiben übrig in meiner Liste. Und dann addiere ich, nehme ich die Länge aller dieser, das ist also eine weitere Map-Funktion. Wir haben also Map, Filter, Map. Also die erste Map-Funktion hat übrigens die Vornamen genommen. Das ist nur in meinem Kopf passiert, das habe ich nicht gesagt. Also die erste Map-Funktion nimmt quasi die Namen aller Centstormer und gibt die Vornamen zurück. Die zweite ist eine Filter-Funktion, die quasi alle, alle, die mit M starten, rausnimmt. Und die dritte ist eine Map-Funktion, die nimmt also die alle und macht Length drauf, die Length-Funktion, die wir eben schon genannt haben. Das heißt, ich habe einen Haufen von Zahlen. Und das vierte ist dann eine Reduce-Funktion, die diese Zahlen alle aufsummiert. Fertig. Und dann hast du eine schöne Kette. Genau, das heißt, du hast eine Kette von die vollständigen Namen aller Centstormer hin zu einer Zahl x. Und diese Zahl x hat nur Bedeutung, überhaupt nur Bedeutung, wenn du dir diese gesamte Kette anguckst. Und dieser Kette kannst du jetzt einen Namen geben. Zum Beispiel den Namen, die Summe der Länge aller Vornamen, die nicht mit M beginnen von Centstormern. Das ist meine Funktionsname und den kann ich jetzt quasi festsetzen. Also den speichere ich mir jetzt weg. Und den kann ich dann aufrufen, wenn ich das mal brauche. Und dahinter ist sozusagen die Komposition dieser vier hintereinander liegenden Funktionen. Und das ist wiederum deterministisch. Das heißt, das kann ich wiederum nehmen und kann das wieder mit was anderem in Verbindung bringen. Wenn es jetzt irgendwie darum geht, irgendwie zu sagen, wir haben irgendwie einen ganzen Haufen an Firmen. Und wir wollen, dass wir alle möglichen Firmen machen. Und dann wollen wir das noch miteinander, okay, wir kommen dazu weiter. Aber grundsätzlich ist das quasi ein gutes Beispiel, was man machen kann, wenn man Pure Functions hat.

Theo
Das klingt toll. Der Sinn dieser Funktion erschließt sich mir sofort. Das ist wirklich eine großartige Funktion. Die benutzen wir auch überall in Produktion.

Martin
Das ist wirklich eine große Art. Die findet man in jedem unserer Programme, wenn man genau danach sucht.

Theo
Genau. Gut, ich glaube, wir haben eine Jam-Pack-Folge mit Informationen. Ich würde es an der Stelle mal, glaube ich, dabei belassen und, wie gesagt, noch mal darauf hinteasen, dass es weitere Folgen dieser Art geben wird. Martin, von dir irgendwelche letzten Worte zum Einstieg in die Funktionellen-Programmierung.

Martin
Letzte Worte zum Einstieg in die funktionale Programmierung. Ja, also lasst euch nicht abschrecken davon, dass das irgendwie alles ein bisschen mathematisch klingt und alles irgendwie fancy Begriffe sind. Tatsächlich ist das Meister ziemlich einfach und es macht einfach einen Haufen Spaß, sich diese Konzepte mal anzugucken. Da helfen vor allem, also es ist für mich der größte Mehrwert, sich mit Konzepten zu beschäftigen, die man vielleicht nicht im Alltag jederzeit benutzt. Immer die Bewusstseinserweiterung und die Horizonderweiterung, die dann in den Alltag zurückschwemmt und man merkt das einfach. Man wird ein besserer Programmierer mit umso mehr Konzepten man sich auseinandergesetzt hat. Das ist meine Meinung.

Theo
Ja, dem schließe ich mich uneingeschränkt an. Super. Dankeschön, Martin, dass du dein tiefes Wissen mit uns geteilt hast. Und ich freue mich auf die nächste Folge dieser kleinen Reihe, die wir geplant haben. Und dann bedanke ich mich außerdem an den Hörern und Hörern und freue mich auf das nächste Sandpapier. Macht's gut, bis dahin. Ciao. Ciao.

Martin
Ja, danke dir Theo, das war lustig, dass du dein tiefes Wissen mit uns geteilt hast und ich freue mich auf die nächste Folge dieser kleinen Reihe, die wir geplant haben und dann bedanke ich mich außerdem den Hörern und Hörern und freue mich auf das nächste Sandpapier. Macht's gut, bis dahin, ciao.

Theo
Das war's für heute.

Dein Besuch auf unserer Website produziert laut der Messung auf websitecarbon.com nur 0,28 g CO₂.