Funktionale Programmierung in Go: Von der Theorie zur Praxis
Go (Golang) wurde ursprünglich nicht als rein funktionale Sprache konzipiert. Dennoch ermöglichen sein starkes Typsystem, die Unterstützung von Funktionen erster Klasse und Closures die erfolgreiche Anwendung von Prinzipien der funktionalen Programmierung (FP). In diesem Artikel werden wir untersuchen, wie man den funktionalen Stil in Go nutzt, um vorhersagbareren, testbareren und nebenläufig sicheren Code zu schreiben.
1. Funktionen erster Klasse und Funktionen höherer Ordnung
In Go sind Funktionen Objekte erster Klasse. Das bedeutet, dass sie Variablen zugewiesen, als Argumente übergeben und von anderen Funktionen zurückgegeben werden können. Funktionen, die mit anderen Funktionen arbeiten, werden Funktionen höherer Ordnung genannt.
Beispiel: Übergabe einer Funktion als Argument
package main
import "fmt"
// Funktion höherer Ordnung: Nimmt eine Funktion entgegen und wendet sie auf eine Zahl anfunc applyOperation(x int, op func(int) int) int { return op(x)}
func double(x int) int { return x * 2}
func square(x int) int { return x * x}
func main() { result1 := applyOperation(5, double) result2 := applyOperation(5, square)
fmt.Println("Double:", result1) // 10 fmt.Println("Square:", result2) // 25}Beispiel: Rückgabe einer Funktion aus einer Funktion (Funktionsfabrik)
package main
import "fmt"
// Gibt eine Funktion zurück, die einen bestimmten Wert hinzufügtfunc createAdder(x int) func(int) int { return func(y int) int { return x + y }}
func main() { addFive := createAdder(5) addTen := createAdder(10)
fmt.Println(addFive(3)) // 8 fmt.Println(addTen(3)) // 13}2. Closures in Go
Ein Closure ist eine Funktion, die sich an Variablen aus dem äußeren Gültigkeitsbereich "erinnert", selbst nachdem dieser Bereich seine Ausführung beendet hat. Closures sind ein mächtiges Werkzeug zur Erstellung von gekapseltem Zustand und Funktionsfabriken.
Beispiel: Sequenzgenerator
package main
import "fmt"
func sequenceGenerator() func() int { i := 0 return func() int { i++ return i }}
func main() { next := sequenceGenerator()
fmt.Println(next()) // 1 fmt.Println(next()) // 2 fmt.Println(next()) // 3
// Neuer Generator beginnt bei 1 next2 := sequenceGenerator() fmt.Println(next2()) // 1}Wichtig: Seien Sie vorsichtig mit Closures in Schleifen. In älteren Go-Versionen (vor 1.22) wurde die Variable i in der Schleife wiederverwendet, was zu Fehlern führte. Ab Go 1.22 werden Schleifenvariablen by value erfasst.
3. Reine Funktionen und Unveränderlichkeit (Immutability)
Eine reine Funktion ist eine Funktion, die:
- Für gleiche Argumente immer das gleiche Ergebnis zurückgibt.
- Keine Seiteneffekte hat (keinen externen Zustand ändert, nicht in Dateien/Netzwerk schreibt).
Go hat keine eingebaute Unterstützung für Unveränderlichkeit, aber wir können diesem Prinzip selbst folgen.
Beispiel: Reine vs. unreine Funktion
package main
import "fmt"
// UNREINE Funktion: Ändert den externen Slicevar counter int
func impureAddToSlice(s []int, val int) []int { counter++ // Seiteneffekt return append(s, val)}
// REINE Funktion: Erstellt einen neuen Slicefunc pureAddToSlice(s []int, val int) []int { newSlice := make([]int, len(s)+1) copy(newSlice, s) newSlice[len(s)] = val return newSlice}
func main() { original := []int{1, 2, 3}
// Reiner Ansatz result := pureAddToSlice(original, 4) fmt.Println("Original:", original) // [1 2 3] - unverändert fmt.Println("Result:", result) // [1 2 3 4]}Tipp: Für die Arbeit mit unveränderlichen Strukturen