Programación funcional en Go: nivel avanzado
\n\nGo (Golang) no es un lenguaje puramente funcional, pero su potente sistema de tipos, soporte para closures y funciones de primera clase permiten aplicar paradigmas funcionales de manera efectiva. A nivel avanzado, la programación funcional en Go ayuda a escribir código más predecible, testeable y seguro para la concurrencia. En este artículo exploraremos conceptos clave: funciones puras, composición, closures, currying, patrones monádicos y manejo de errores sin excepciones.
\n\n1. Funciones puras e inmutabilidad
\n\nUna función pura es una función que:
\n- \n
- Siempre devuelve el mismo resultado para los mismos argumentos (determinismo). \n
- No tiene efectos secundarios (no modifica el estado externo, no escribe en archivos/red). \n
En Go, la inmutabilidad se logra mediante la transferencia de copias de datos y el uso de constantes. Ejemplo de función pura:
\n\npackage main\n\nimport "fmt"\n\n// Función pura: no modifica el slice original, devuelve uno nuevo\nfunc DoubleSlice(s []int) []int {\n result := make([]int, len(s))\n for i, v := range s {\n result[i] = v * 2\n }\n return result\n}\n\nfunc main() {\n original := []int{1, 2, 3}\n doubled := DoubleSlice(original)\n fmt.Println(original) // [1 2 3] — sin cambios\n fmt.Println(doubled) // [2 4 6]\n}\n\nEl uso de funciones puras simplifica la refactorización y las pruebas, ya que no es necesario considerar el estado oculto.
\n\n2. Funciones de orden superior y closures
\n\nEn Go, las funciones son objetos de primera clase: se pueden pasar como argumentos, devolver desde otras funciones y almacenar en variables. Un closure es una función que captura variables del ámbito externo.
\n\nEjemplo de función de orden superior — Filter:
package main\n\nimport "fmt"\n\n// Filter devuelve un nuevo slice con los elementos que cumplen el predicado\nfunc Filter[T any](s []T, predicate func(T) bool) []T {\n var result []T\n for _, v := range s {\n if predicate(v) {\n result = append(result, v)\n }\n }\n return result\n}\n\nfunc main() {\n numbers := []int{1, 2, 3, 4, 5, 6}\n even := Filter(numbers, func(n int) bool { return n%2 == 0 })\n fmt.Println(even) // [2 4 6]\n}\n\nLos closures se usan a menudo para crear fábricas de funciones o para encapsular estado:
\n\npackage main\n\nimport "fmt"\n\n// Counter crea un closure que almacena estado interno\nfunc Counter() func() int {\n count := 0\n return func() int {\n count++\n return count\n }\n}\n\nfunc main() {\n c1 := Counter()\n fmt.Println(c1()) // 1\n fmt.Println(c1()) // 2\n\n c2 := Counter()\n fmt.Println(c2()) // 1 — estado independiente\n}\n\n3. Composición de funciones y aplicación parcial (currying)
\n\nLa composición es la combinación de varias funciones en una nueva. En Go, la composición se implementa mediante funciones intermedias. La aplicación parcial (currying) permite fijar parte de los argumentos y devolver una función que espera los restantes.
\n\nEjemplo de composición:
\n\npackage main\n\nimport "fmt"\n\n// Compose combina dos funciones: f(g(x))\nfunc Compose[A, B, C any](f func(B) C, g func(A) B) func(A) C {\n return func(x A) C {\n return f(g(x))\n }\n}\n\nfunc addOne(x int) int { return x + 1 }\nfunc double(x int) int { return x * 2 }\n\nfunc main() {\n addThenDouble := Compose(double, addOne)\n fmt.Println(addThenDouble(3)) // (3+1)*2 = 8\n}\n\nCurrying con un ejemplo de función de suma:
\n\npackage main\n\nimport "fmt"\n\n// Add — versión currificada de la suma\nfunc Add(a int) func(int) int {\n return func(b int) int {\n return a + b\n }\n}\n\nfunc main() {\n addFive := Add(5)\n fmt.