Por qué la Programación Funcional en Go es el Estilo de Código Definitivo

Cuando piensas en programación funcional, seguramente te vienen a la mente lenguajes como Haskell (con sus funciones puras y mónadas) o JavaScript (con sus funciones de orden superior y callbacks).

Pero, ¿Sabías que también puedes aplicar programación funcional en Go? Aunque no sea lo más común, Go permite escribir código funcional de manera eficiente y elegante.

A continuación, exploraremos conceptos clave de la programación funcional en Go con ejemplos prácticos.

Funciones de Orden Superior

Las funciones de orden superior son aquellas que pueden recibir otras funciones como parámetros o devolverlas como resultado.

Ejemplo: Filtrar números pares

package main

import (
    "fmt"
)

func filter(numbers []int, f func(int) bool) []int {
    var result []int
    for _, value := range numbers {
        if f(value) {
            result = append(result, value)
        }
    }
    return result
}

func isEven(n int) bool {
    return n%2 == 0
}

func main() {
    numbers := []int{1, 2, 3, 4}
    even := filter(numbers, isEven)
    fmt.Println(even) // [2, 4]
}

Aquí, la función filter recibe un slice de enteros y una función que evalúa una condición (isEven). El resultado es una lista con los elementos que cumplen esa condición.

⚡ Currificación (Currying)

La currificación consiste en transformar una función de múltiples argumentos en una serie de funciones, cada una de las cuales recibe un solo argumento.

Ejemplo: Función para sumar números

package main

import "fmt"

func add(a int) func(int) int {
    return func(b int) int {
        return a + b
    }
}

func main() {
    addFive := add(5)
    fmt.Println(addFive(3)) // 8
}

Aquí, add(5) devuelve una función que suma 5 a cualquier número que reciba.

Inmutabilidad

Uno de los principios fundamentales de la programación funcional es la inmutabilidad: una vez que se crea un objeto, no se modifica. En lugar de alterar los datos originales, se crean nuevas estructuras de datos.

Ejemplo: Clonar y modificar un mapa

package main

import "fmt"

func main() {
    obj := map[string]int{"a": 1, "b": 2}
    newObj := make(map[string]int)
    for k, v := range obj {
        newObj[k] = v
    }
    newObj["b"] = 3
    fmt.Println(newObj) // map[a:1 b:3]
}

Aquí, en lugar de modificar obj, creamos una copia newObj y la editamos.

Funciones Puras

Las funciones puras son aquellas que no modifican estados globales y siempre devuelven el mismo resultado para los mismos parámetros.

Ejemplo: Función pura para elevar un número al cuadrado

package main

import "fmt"

func square(x int) int {
    return x * x
}

func main() {
    fmt.Println(square(5)) // 25
}

La función square no depende de variables externas y siempre devuelve el mismo resultado.

Functores

Un functor es una estructura que puede aplicar una función a sus elementos y devolver una nueva estructura con los resultados.

Ejemplo: Mapear una lista de números

package main

import "fmt"

func mapInts(values []int, f func(int) int) []int {
    result := make([]int, len(values))
    for i, v := range values {
        result[i] = f(v)
    }
    return result
}

func main() {
    numbers := []int{1, 2, 3, 4}
    squared := mapInts(numbers, func(x int) int { return x * x })
    fmt.Println(squared) // [1, 4, 9, 16]
}

Aplicamos una función (x * x) a cada elemento de la lista y obtenemos un nuevo slice con los resultados.

Monoides

Un monoide es una estructura que..

✅ Tiene una operación asociativa (puede combinarse con otros valores de la misma estructura).
✅ Tiene un elemento neutro que no altera el resultado.

Ejemplo: Suma de números

package main

import "fmt"

func add(a, b int) int {
    return a + b
}

func main() {
    fmt.Println(add(5, 5))  // 10
    fmt.Println(add(5, 0))  // 5
    fmt.Println(add(0, 0))  // 0
}

El 0 es el elemento neutro en la suma, porque no altera el resultado.

Mónadas

Las mónadas son una forma de encadenar funciones que trabajan con valores dentro de un contexto (como errores o listas).

Ejemplo: Mónada para manejar errores en Go

package main

import (
    "errors"
    "fmt"
)

func Maybe(value int, err error, f func(int) (int, error)) (int, error) {
    if err != nil {
        return 0, err
    }
    return f(value)
}

func main() {
    process := func(v int) (int, error) {
        if v < 0 {
            return 0, errors.New("valor negativo")
        }
        return v * v, nil
    }

    result, err := Maybe(5, nil, process)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Éxito:", result) // Éxito: 25
    }
}

Esta “mónada” nos ayuda a manejar errores sin romper el flujo del programa.

Conclusión

Aunque Go no es un lenguaje funcional puro, sí permite aplicar principios de programación funcional para escribir código más limpio, reutilizable y mantenible. ¿Te animas a usar más programación funcional en Go?

Vistas: 3

Descubre más desde CIBERED

Suscríbete y recibe las últimas entradas en tu correo electrónico.

Scroll al inicio