9.3. Imperativ und funktional#

Funktionale Programmiersprachen bieten hohe Sicherheit indem sie sogenannte Seiteneffekte nicht erlauben und anstatt Variablen (abänderbar/mutable) nur Konstanten (unabänderlich/immutable) zulassen.

Seiteneffekt

Übergeben Sie eine Variable oder eine Datenstruktur einer Funktion und verändert diese Funktion jene Variable oder Datenstruktur, dann sprechen wir von einem Seiteneffekt.

In imperativen Programmiersprachen (Python, Java, C#, C++, C, JavaScript, …) sind Seiteneffekte manchmal erwünscht und auch notwendig. Folgender Code zeigt einen Seiteneffekt in Python.

y = []
def sideeffect(x):
    x += [1,2,3]
    return x

print(y)
print(sideeffect(y))
print(y)
[]
[1, 2, 3]
[1, 2, 3]

Die Liste y (Datenstruktur) wird durch die Funktion sideeffect befüllt.

Dies ist in der funktionalen Programmiersprache Haskell nicht möglich! Wir können den obigen Seiteneffekt in Python wie folgt auflösen.

y = [-10]
def no_sideeffect(x):
    x = x + [1,2,3]
    return x

print(y)
print(no_sideeffect(y))
print(y)
[-10]
[-10, 1, 2, 3]
[-10]

In diesem Fall bleibt y unberührt, stattdessen wird eine neue Liste (eine Kopie) von y) befüllt.

Funktionale Sprachen umgehen Seiteneffekte indem sie keine Datenstrukturen ändern, sondern stattdessen neu erzeugen. Fügt man ein neues Element in eine Liste an, entsteht eine komplett neue Liste. Funktionale Sprachen verzichten auf Variablen und realisieren Operationen durch Konstanten und sog. Pure Functions (mathematische Funktionen).

Reine Funktion (pure Function)

Eine Funktion nennen wir reine Funktion wenn

  1. Der Rückgabewert der Funktion für die gleiche Funktionsargumente stets identisch ist.

  2. Wenn die Funktion keine Seiteneffekte hat.

Reine Funktionen verhalten sich deshalb wie mathematische Funktionen. Die folgende Funktion ist eine reine Funktion.

def f(x):
    return x + 1

print(f(2))
3

Folgende Funktion ist keine reine Funktion.

y = 2
def f(x):
    if y == 2:
        return x + 1
    else:
        return x

print(f(2))
y = 1
print(f(2))
3
2

Reine Funktion und Python

Python macht es uns schwer keine reine Funktion zu konstruieren. Zum Beispiel würde folgender Code zu einem Fehler führen:

y = 0
def f(x):
    y = y + 1
    return x + y

print(f(2))
print(f(2))

Exercise 9.3 (Seiteneffekte)

Weshalb könnte es Sinn machen Seiteneffekte zuzulassen? Weshalb macht es zugleich Sinn sie so gut es geht zu vermeiden?

Wir besprechen reine Funktionen in Python im Abschnitt Reinheit.

Funktionale Sprachen wie Haskell waren anfänglich rein akademische Sprachen mit denen sich in der Praxis nichts wirklich anfangen ließ. Nichtsdestotrotz haben diese Sprachen interessante Konzepte zutage gebracht. Von „extrem sicher aber unbrauchbar“ wandern funktionale Sprachen Schritt für Schritt in Richtung „extrem sicher und brauchbar“.

Imperative Sprachen hingegen wandern tendenziell von „sehr brauchbar aber unsicher“, zu „sehr brauchbar und sicher“. Beide Lager lernen voneinander. So existieren in Python und allen anderen sehr nützlichen Sprachen wie Java, C#, C++ Konstrukte, die von funktionalen Sprachen inspiriert sind. Zum Beispiel erzeugt

def square(x):
    return x * x

list(map(square, [1,2,3]))
[1, 4, 9]

eine neue Liste mit den quadrierten Zahlen der Ursprungsliste. Das besondere dabei ist, dass wir die Funktion square einer anderen Funktion map übergeben. Funktionen als Übergabeparameter (sog. Funktion erster Klasse) ist ein Konzept der funktionalen Sprachen. In Haskell sieht das ganze wie folgt aus:

square :: Int -> Int
square x = x * x
map square [1, 2, 3]