domingo, 8 de marzo de 2026

Programación funcional en Haskell

Uno de los puntos fuertes de Haskell es la programación funcional pura. La cual se centra en funciones matemáticas puras, sin efectos secundarios, y en transformar datos de manera predecible.

Donde cada función es como una receta que recibe ingredientes (datos de entrada) y devuelve un resultado (salida).

Donde las funciones no alteran cosas externas (osea, sin efectos secundarios), solo trabajan con lo que reciben.

Donde los datos no cambian, se transforman en nuevos resultados.

Donde no se evalúa una expresión hasta que su resultado sea estrictamente necesario, permitiendo el manejo de estructuras de datos infinitas.

Con Haskell es como si trabajaras con fórmulas matemáticas, cada función es una operación que siempre da el mismo resultado con los mismos datos.

Incluso permitiendo que las funciones tomen a otras funciones como argumentos, o simplemente devolviendo éstas.

Es como hacer un jugo de naranja. Tomas la naranja y la exprimes, y así obtienes su jugo. Repitiendo el mismo proceso. Sin explicar ni detallar absolutamente nada.

Función jugo(naranja) -> "jugo de naranja"

Para programadores que usan Java esto puede parecer confuso. Puesto que nos llevaría una lógica muy distinta:

boolean hayNaranjas = true;

if(hayNaranjas){
   irAlaCocina();
   tomarLaNaranja();
   prepararVaso();
   exprimirNaranja();
   servirVaso();
}

Para entender mejor esto tenemos lo siguiente...

Los puntos fuertes de la programación funcional pura (en Haskell) son:

1. Las funciones puras: una función siempre devuelve el mismo resultado; no depende de variables externas ni modifica estados.

2. Inmutabilidad: no existen as mutaciones de estado, los valores no cambian.

3. Transparencia referencial: una expresión puede ser reemplazada por su valor sin alterar el comportamiento del programa.

4. Evaluación perezosa (lazy evaluation): las expresiones se evalúan solo cuando son necesarias.

5. Tipado fuerte y estático: el compilador verifica tipos en tiempo de compilación

Para entender mejor crearemos un programa que defina una función ``suma`` que sume dos números enteros (Int).

suma.hs

suma :: Int -> Int -> Int
suma x y = x + y

La función ``suma`` es pura y siempre que se invoque con los mismos argumentos, devolverá el mismo resultado. Es decir, si introduces dos números enteros, devolverá un resultado tipo entero. Quien haya asistido a la escuela primaria y llevado la materia de aritmética básica entenderá esto.

Entremos al REPL de Haskell:

$ ghci

Cargamos el programa:

ghci> :l suma.hs
[1 of 2] Compiling Main             ( suma.hs, interpreted )
Ok, one module loaded.

Introducimos dos valores tipo entero (Int) y el resultado será entero:

ghci> suma 45 32
77
ghci> suma 1 5
6

Ahora un ejemplo de evaluación perezosa con el programa ``naturales.hs``, en el cual definimos una lista infinita:

naturales :: [Int]
naturales = [1..]

primerosCinco = take 5 naturales

Cargamos el programa:

ghci> :l naturales.hs
[1 of 2] Compiling Main             ( naturales.hs, interpreted )
Ok, one module loaded.

La lista naturales es infinita, pero gracias a la evaluación perezosa podemos trabajar con ella sin problemas.

Salida:

ghci> primerosCinco
[1,2,3,4,5]

Continuaremos con este tema en próximas entregas.

Enlaces:

http://aprendehaskell.es/
https://wiki.haskell.org/index.php?title=Functional_programming

domingo, 1 de marzo de 2026

Tipos de datos en Haskell

Como hemos mencionado, Haskell es un lenguaje de programación funcional de tipado estático. ¿Qué quiere decir esto?

  • En Haskell es necesario especificar qué tipos de datos vamos a emplear en nuestros programas. 
  • Puesto que al compilar el programa (sí, Haskell se compila) se evaluarán los tipos para saber si son enteros, booleanos o cadenas de caracteres.
  • Además al tener inferencia de tipos no tenemos que especificar, por ejemplo, que un número es un número.

Comprobemos esto. Abrimos una terminal y tecleamos:

$ ghci

Esto abrirá el REPL de Haskell. Usemos el comando :type o el abreviado :t para ver los tipos de datos.

ghci> :type "Esto es una cadena"
"Esto es una cadena" :: String
ghci> :type 33
33 :: Num a => a
ghci> :type True
True :: Bool
ghci> :t 'X'
'X' :: Char
ghci> :t 23 + 32
23 + 32 :: Num a => a

En este ejemplo vemos los tipos: Char, String, Num y Bool.

Incluso aplica en las propias funciones de Haskell:

ghci> :type succ
succ :: Enum a => a -> a
ghci> :type max
max :: Ord a => a -> a -> a
ghci> :type min
min :: Ord a => a -> a -> a

Incluso nuestras propias definiciones de función:

ghci> :l factorial.hs
[1 of 2] Compiling Main             ( factorial.hs, interpreted )
Ok, one module loaded.
ghci> :type factorial
factorial :: Int -> Int

Hay más tipos de datos, incluso podrías crear los tuyos. Similar a la lógica de los typedef de C.

Para ello tenemos a data, newtype y type . Veamos unos ejemplos.

Ejemplos con data

Tipos algebraicos de datos. Permiten crear estructuras que pueden tener múltiples constructores.

data Color = Rojo | Verde | Azul

Usar Maybe para representar valores opcionales.

data Maybe a = Nothing | Just a

Los registros permiten definir tipos con campos con nombre, lo que facilita el acceso y la actualización.

data Persona = Persona { nombre :: String, edad :: Int }

Podemos crear "objetos" y acceder a sus "propiedades". Similar a lo que se hace en C con los struct.

juan = Persona { nombre = "Juan", edad = 30 }

Ejemplos con type

Con ellos puedes crear alias de tipos, útiles para dar más claridad.

type Nombre = String
type Edad = Int
type Persona = (Nombre, Edad)

Ejemplos con newtype

Útiles cuando quieres crear un tipo distinto pero basado en otro, con costo cero en tiempo de ejecución.

newtype UsuarioId = UsuarioId Int

En este caso, esto evita confusiones entre enteros que representan cosas diferentes.

Hacer uso de esto es muy útil para cuando necesitemos usar estructuras complejas, alias más legibles o datos más seguros.

Haskell tiene tipos Int e Integer para representar números enteros.

Float para los números reales en coma flotante.

Double para los números reale en coma flotante de doble precisión.

Y demás tipos útiles en nuestros programas, además de permitir nuestras propias definiciones de datos.

Vamos a crear un programa en Haskell que muestre los tipos básicos como Int, Integer, Float, Double, Char, String y Bool.

tipos_datos.hs

-- Programa en Haskell para mostrar tipos de datos básicos
main :: IO ()
main = do
    -- Int: enteros con tamaño fijo
    let xInt :: Int
        xInt = 42
    putStrLn ("Int: " ++ show xInt)

    -- Integer: enteros arbitrariamente grandes
    let xInteger :: Integer
        xInteger = 123456789012345678901234567890
    putStrLn ("Integer: " ++ show xInteger)

    -- Float: números en coma flotante de precisión simple
    let xFloat :: Float
        xFloat = 3.14159
    putStrLn ("Float: " ++ show xFloat)

    -- Double: números en coma flotante de precisión doble
    let xDouble :: Double
        xDouble = 2.718281828459045
    putStrLn ("Double: " ++ show xDouble)

    -- Char: un solo carácter
    let xChar :: Char
        xChar = 'A'
    putStrLn ("Char: " ++ show xChar)

    -- String: lista de caracteres
    let xString :: String
        xString = "Hola, Haskell!"
    putStrLn ("String: " ++ xString)

    -- Bool: valores booleanos
    let xBoolTrue :: Bool
        xBoolTrue = True
    let xBoolFalse :: Bool
        xBoolFalse = False
    putStrLn ("Bool (True): " ++ show xBoolTrue)
    putStrLn ("Bool (False): " ++ show xBoolFalse)

Entramos al REPL de Haskell:

$ ghci

Cargamos el programa y lo ejecutamos. La salida será la siguiente:

ghci> :l tipos_datos.hs
[1 of 2] Compiling Main             ( tipos_datos.hs, interpreted )
Ok, one module loaded.
ghci> main
Int: 42
Integer: 123456789012345678901234567890
Float: 3.14159
Double: 2.718281828459045
Char: 'A'
String: Hola, Haskell!
Bool (True): True
Bool (False): False

Haskell cuida que los tipos de datos sean los correctos, y esto ocurre en el tiempo de compilación. Asegurando que el programa cumpla con los criterios de definición de datos. Lo que asegura que nuestro prograa funcionará correctamente (ahí te hablan Python).

Enlaces:

https://wiki.haskell.org/index.php?title=Type
https://serokell.io/blog/kinds-and-hkts-in-haskell

WebAssembly con Haskell

WebAssembly es un lenguaje de bajo nivel, similar a ensamblador, diseñado para ser rápido de cargar y ejecutar en navegadores modernos. ...