Haskell Hero

Haskell Hero es un manual interactivo del lenguaje Haskell para principiantes.

La aplicación parcial

Empezamos

En una de las primeras lecciones hemos hablado sobre la aridad de funciones. Hemos dicho que tenemos funciones unarias, binarias, ternarias, etc. Ahora mostramos que la aridad la podremos ver también de una manera diferente.

Tengamos una función ternaria general f x y z de un tipo general f :: a -> b -> c -> d. Esta función necesita, según lo que sabemos, tres parámetros para que se evalúe completamente.

Sin embargo, ¿qué pasa si la llamamos solo con un parámetro? Se convierte de una función ternaria a una función binaria (f x) :: b -> c -> d.

¿Qué pasa si llamamos la función binaria (f x) con un parámetro? Se convierte en una función unaria (f x y) :: c -> d.

Y por fin, ¿qué pasa si llamamos la función unaria (f x y) con un parámetro? Se convierte en una función nularia (f x y z) :: d.

Si escribimos esta expresión en Hugs, la pone entre paréntesis como ((f x) y) z y realiza una aplicación gradual mencionada arriba.

Ejemplo con (+)

(+) es una función binaria que toma dos números y devuelve su suma.


La función (+) representada por una caja

¿Qué pasa si la llamamos con un solo parámetro, por ejemplo con 5? Se convierte en una función unaria ((+) 5) que toma un número como parámetro y lo suma con 5.


La función (5+) representada por una caja

((+) 5) 3 ~> 5 + 3 ~> 8
((+) 5) 4 ~> 5 + 4 ~> 9
((+) 5) 6 ~> 5 + 6 ~> 11
La función ((+) 5) la podemos escribir en la notación infija como (5+) igual que podemos escribir (div 4) como (4 `div`).

Ya que la adición es conmutativa, da igual si escribimos 3 + 5 o 5 + 3. Sin embargo, podemos encontrar un problema por ejemplo trabajando con la función (^) porque aquí ya es importante si se realiza 2^x o x^2. La función que devuelve 2^x se define como (2^) y la función que calcula x^2 se define como (^2).

(2^) 5 ~>* 32
(^2) 5 ~>* 25

Ejemplo con zipWith

Hemos dicho que la función zipWith es ternaria. Esto significa que necesita tres parámetros para su evaluación completa. ¿Qué pasa si la llamamos con un parámetro, por ejemplo con la función (*)? Se convierte un una función binaria (zipWith (*)) que toma dos listas y multiplica sus elementos entre sí.

¿Qué pasa si aplicamos la función (zipWith (*)) por ejemplo a la lista [1,2,3]? Se convierte en una función unaria (zipWith (*) [1,2,3]) que toma una lista y devuelve una lista de tres parámetros como máximo en la que está el primer parámetro de la lista tomada, el doble del segundo parámetro y el triple del tercero parámetro.

¿Qué pasa si aplicamos la función (zipWith (*) [1,2,3]) a la lista [5,5,5,5]? Se convierte en una función nularia zipWith (*) [1,2,3] [5,5,5,5], lo que es una expresión que ya se puede evaluar completamente y se evalúa a la lista [5,10,15].

zipWith
   :: (a -> b -> c) -> [a] -> [b] -> [c]

zipWith (++)
   :: [[a]] -> [[a]] -> [[a]]

zipWith (++) [[1,2], [3,4,5]]
   :: [[Integer]] -> [[Integer]]

zipWith (++) [[1,2], [3,4,5]] [[10,11,12], [13,14]]
   :: [[Integer]]
La última expresión se evalúa de manera siguiente:
    zipWith (++) [[1,2], [3,4,5]] [[10,11,12], [13,14]]

~>* [[1,2] ++ [10,11,12], [3,4,5] ++ [13,14]]
~>* [  [1,2,10,11,12]   ,   [3,4,5,13,14]   ]