¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Funciones y tipos en Haskell

Después de esta lección: Puedo leer una función pequeña de Haskell y su tipo.

Lección de 55 min11 comandosLaboratorio de Haskell: programación funcional con funciones y tipos, en el navegador

Programar solo con funciones que devuelven valores

Haskell es un lenguaje de programación funcional: un programa es un conjunto de funciones que reciben valores y devuelven valores. Cada función lleva un tipo que dice qué puede recibir y qué devuelve, y el compilador lo comprueba antes de ejecutar nada.

Así se descubren muchos errores antes de que un cliente los encuentre, por ejemplo sumar un precio a un nombre.

Al final de esta lección

Nota

Puedo leer una función pequeña de Haskell y su tipo.

Palabras clave

PalabraQué significaEjemplo
Función purauna función que con los mismos datos da siempre el mismo resultado
Tipola clase de un valor: número, texto, sí o noInt
Firma de tipola línea con :: que dice qué recibe y qué devuelvesquare :: Int -> Int
Composiciónunir dos funciones: lo que sale de una entra en la otraf . g
Expresiónalgo que se calcula hasta dar un valorsquare 7

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Una función pura solo depende de sus datos

Una función pura no mira nada fuera de sus datos y no cambia nada fuera. Si le das el mismo valor, devuelve siempre el mismo resultado.

square 7 es 49 hoy y mañana. En Haskell las funciones son puras, así que se pueden probar una a una y se leen sin miedo a efectos escondidos.

Dos funciones sobre precios

Código: haskell
-- Los precios van en céntimos, para no usar decimales.
withVat :: Int -> Int
withVat price = div (price * 121) 100

total :: Int -> Int -> Int
total price units = price * units

-- withVat 1000 es 1210. total 250 3 es 750.

Lee las funciones del laboratorio

Comando
docker exec haskell cat Basics.hs

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejecuta el archivo entero

Comando
docker exec haskell runghc Basics.hs

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

La firma dice qué entra y sale

La línea isEven :: Int -> Bool se lee así: isEven recibe un Int (un número entero) y devuelve un Bool (verdadero o falso).

Con varios datos, la flecha final es el resultado. Int -> Int -> Int recibe dos enteros y devuelve uno. Antes de ejecutar, Haskell comprueba que cada llamada respeta la firma.

Ejemplo: cómo leer tres firmas

FirmaRecibeDevuelve
square :: Int -> IntUn enteroUn entero
isEven :: Int -> BoolUn enteroVerdadero o falso
greet :: String -> StringUn textoUn texto
total :: Int -> Int -> IntDos enterosUn entero

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Pregunta el tipo de square

Comando
docker exec haskell ghc -e ":type square" Basics.hs

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Pregunta el tipo de greet

Comando
docker exec haskell ghc -e ":type greet" Basics.hs

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Pregunta el tipo de una función ajena

Comando
docker exec haskell ghc -e ":type not"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Se llama a una función con un espacio

En Haskell no hay paréntesis para llamar: square 7 es la llamada, donde Python escribiría square(7). Los paréntesis solo agrupan.

Una llamada es una expresión: un valor calculado. Con ghc -e calculas una expresión suelta y ves su valor. Si añades un archivo al final, sus funciones quedan cargadas.

Llama a square con el 7

Comando
docker exec haskell ghc -e "square 7" Basics.hs

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Llama a greet con un nombre

Comando
docker exec haskell ghc -e "greet \"Lucia\"" Basics.hs

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Un tipo equivocado se rechaza

Comando
docker exec haskell ghc -e "greet 5" Basics.hs

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

El punto une dos funciones

f . g es la composición de dos funciones: una función nueva que aplica primero g y después f. Se lee de derecha a izquierda, como en matemáticas.

(square . square) 3 calcula square 3, que es 9, y después square 9, que es 81.

Dos cuadrados seguidos

Comando
docker exec haskell ghc -e "(square . square) 3" Basics.hs

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Primero multiplicar y después sumar

Comando
docker exec haskell ghc -e "((+ 1) . (* 2)) 5"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Primero sumar y después multiplicar

Comando
docker exec haskell ghc -e "((* 2) . (+ 1)) 5"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Un error frecuente

Advertencia

Mucha gente cree que (f . g) aplica primero f, porque se lee de izquierda a derecha. No es así: aplica primero g. El orden cambia el resultado, como muestran los dos últimos Runs: 11 y 12.

En Python una función también es un valor

Código: python
# Una función es un valor: se puede pasar a otra función.
def square(n):
    return n * n


def add_one(n):
    return n + 1


def run(f, value):
    return f(value)


print(run(square, 7))
print(run(add_one, 7))
# Cambia run(add_one, 7) por run(square, 12) y vuelve a ejecutar.

Para ir más lejos

Mira la firma de (.) con ghc -e ":type (.)". Dice (b -> c) -> (a -> b) -> a -> c. Las letras minúsculas son tipos cualquiera.

Explica con una frase por qué esa firma obliga a que lo que devuelve g sea lo que recibe f. Después piensa por qué greet 5 no llega a ejecutarse.

Sobre este laboratorio y su código: Laboratorio de Haskell: programación funcional con funciones y tipos, en el navegador

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.