Unidad III - 2 - Haskell
Unidad III - 2 - Haskell
Unidad III - 2 - Haskell
No hay, una acepción precisa sobre el término "funcional". Pero cuando decimos que
Haskell es un lenguaje funcional, solemos tener en cuenta dos cosas:
1. Las funciones tienen aquí preponderancia, y estas se pueden utilizar de la
misma forma que cualquier otro tipo de valor.
2. El significado de los programas Haskell se centra en la evaluación de
expresiones, en lugar de ejecutar instrucciones.
Esto resulta en una forma completamente diferente de pensar a la programación.
Puro
Perezoso
En Haskell, las expresiones no se evalúan, hasta que sus resultados son realmente
necesarios. Esta es una característica sencilla, con consecuencias de gran alcance, lo
cual vamos a explorar. Algunas de las consecuencias son:
Tipos
Tipado estáticamente
Cada expresión Haskell tiene asociado un tipo, dichos tipos son controlados en tiempo
de compilación. Los programas con errores de tipo no corren, ni siquiera se compilan
exitosamente.
Los sistemas de tipos estáticos pueden parecer tediosos. De hecho, pasa también en
lenguajes como C ++ y Java. Pero esto se debe a que los sistemas de tipo estático
como el C ++ y Java son insuficientemente expresivos1. Vamos a echar un vistazo de
cerca al sistema de tipos de Haskell, que:
Ayuda a clarificar el pensamiento y a estructurar un programa expresivo.
El primer paso para escribir un programa Haskell suele ser anotar todos los
tipos. Porque el sistema de tipos de Haskell es expresivo, este es una etapa no
trivial del diseño.
Sirve como una forma de documentación de un sistema de tipo expresivo, sólo al
mirar el tipo de una función dice mucho acerca de lo que la función puede hacer y
cómo se puede utilizar, incluso antes de haber leído una sola palabra de la
documentación escrita.
Convierte errores en tiempo de ejecución en errores en tiempo de compilación.
Es mucho mejor poder corregir los errores en la cabecera, que simplemente
probar sucesivamente y esperar lo mejor (aun así es muy posible tener errores en
la lógica, incluso en un programa tipado correctamente).
1
Un programa fuente es expresivo cuando no hace falta aclarar nada, sino que se entiende con solo leerlo. Un
lenguaje es expresivo si permite crear programas fuentes expresivos.
Abstracción
Programación integral
La programación integral implica pensar en grande: trabajar con una lista completa,
en lugar de una secuencia de elementos; desarrollar un espacio de soluciones, en
lugar de una solución individual; imaginar una trayectoria, en lugar de un tramo de
esta. El enfoque integral a menudo ofrece nuevos puntos de vista u ofrecen nuevas
perspectivas sobre un problema dado. Está muy bien complementado por la idea de la
programación proyectiva: primero resolver un problema más general, a continuación,
extraer las partes y piezas interesantes mediante la transformación del programa
general en otras más especializadas.
Por ejemplo, considere este pseudocódigo en C o Java:
lst = {2,3,5,7,11}
int total = 0;
for ( int i = 0; i < lst.length; i++) {
total = total + 3 * lst[i];
}
print total;
Este código tiene que preocuparse por los detalles de bajo nivel de iteración del arreglo
mediante el seguimiento de un índice. También se mezcla lo importante con lo
accesorio necesarios para implementar el ciclo repetitivo.
Declaraciones y Variables
y :: Int
y = y + 1
main = print y
Debido a que = denota definición en lugar de asignación, esta no se incrementa el valor
de y. En su lugar, esta afirmación se toma como una definición recursiva.
y = y + 1
= (y + 1) + 1
= ((y + 1) + 1) + 1
=
.
.
.
En definitiva, el resultando es un loop infinito y el consiguiente error:
-- Booleanos
b1, b2 :: Bool
b1 = True
b2 = False
-- caracteres Unicode
c1, c2, c3 :: Char
c1 = 'x'
c2 = 'Ø'
c3 = 'ダ'
2
Note que Haskell utiliza la convención CamelCase en la forma de darle nombre a los identificadores
Lógica Booleana
Los valores booleanos se pueden comparar con (&&), (| |), y not (and, or y not lógicos,
respectivamente).
es una expresión que se evalúa como t si la expresión booleana b se evalúa como True,
y f si b se evalúa como False. Se debe tener en cuenta que las expresiones if son muy
diferentes de las sentencias if. Por ejemplo, con una sentencia if, la parte else puede ser
opcional; si el else es omitido significa que "si la expresión se evalúa como False, no se
hará nada", en cambio, en la expresión if, se requiere la parte else, de la expresión if
debe dar lugar a un cierto valor.
Uso de funciones
Para aplicar los argumentos a una función, sólo se listan los argumentos después de la
función, separados por espacios, por ejemplo:
f x y z = x + y + z
main = print (f 3 17 8)
El ejemplo anterior se aplica la función f con los argumentos 3, 17, y 8.
Tenga en cuenta que la aplicación de funciones tiene mayor precedencia que cualquier
operador infijo. Por lo que, si tiene la intención de pasar n 1 como el segundo argumento
de f, sería incorrecto escribir f 3 n 1 7. En lugar de ello, uno debe escribir f 3 (n 1) 7.
Construcción de Listas
La lista más simple posible es la lista vacía:
emptyList = []
Otras listas se construyen a partir de la lista vacía utilizando el operador (:). Este opera
un elemento y una lista, y produce una nueva lista con el nuevo elemento al frente.
a = 1 : []
b = 3 : (1 : [])
c = [2,3,4] == 2 : 3 : 4 : []
La segunda clausula dice que si la lista de entrada se parece a (x:xs), que es, un primer
elemento x y el resto de la lista xs, entonces la longitud es 1+ la longitud de xs.
[3,7,11,15]
Nótese cómo la última cláusula coincide con una lista que comienza con x y seguido por
... una lista a partir de y, y seguido por la lista zs. En realidad no necesitamos los
paréntesis adicionales, por lo que sumarCadaDos (x:y:zs) = ... debería ser equivalente.
Combinando Funciones
Es un buen estilo de Haskell construir funciones complejas mediante la combinación de
muchas simples.
Abstracciones lambda
En lugar de usar ecuaciones para definir funciones, podemos definirlas en forma
"anónima" vía una lambda abstracción. Por ejemplo, podemos escribir una función
equivalente a incremento (inc) en la forma \x -> x+1. Del mismo modo, la función add es
* cualquier subcadena
:browse all Muestra todos los nombres en su ámbito dentro de los módulos. Si no se
ven los módulos, solo describe el módulo actual. También indica tipos, parámetros y
salidas de las funciones dentro del ámbito.
:reload
Borrar todos los archivos excepto los módulos vacíos Prelude de Haskell 98 y los
módulos que utiliza, y vuelve a cargar todos los módulos cargados previamente.
: edit [archivo]
: find [archivo]
:! [command]
Invoca el Shell con el comando indica. Si el comando es omitido abre una sesión del
shell.
:set -t
:set +t
Fuentes:
https://www.fpcomplete.com/school/starting-with-haskell/introduction-to-haskell/1-
haskell-basics
https://www.haskell.org/hugs/pages/users_guide/info-commands.html
http://aprendehaskell.es/