Lenguaje Haskell. Conceptos y Estructuras básicas
Sintaxis básica de Haskell
En un lenguaje funcional, un programa consistirá en evaluar una función. Por tanto, veamos como se realiza la evaluación de funciones en un lenguaje funcional particular: Haskell.
En este capítulo vamos a estudiar los tipos de datos que podemos usar, los operandos y operaciones así como su aritmética.
Todo ello se ilustrará con los correspondientes ejemplos en el último punto.
En la Tabla 1 se muestran los distintos tipos que ofrece el lenguaje.
Tipo |
Descripción |
Int |
Entero: 2, 3, -1, 0, ... |
Float |
Real: 0.5, 1, -0.45, ... |
Char |
Carácter: ‘a’, ‘b’, ‘F’, ... |
Bool |
Booleano: ‘true’ o ‘false’ |
También podemos tener cadenas de caracteres que se pueden definir como listas de caracteres, las cadenas de caracteres se escriben mediante comillas dobles: "hola".
La forma de escribir una función en Haskell se define a continuación:
En la definición de la signatura se utiliza la siguiente sintaxis:
nombre_funcion::tipo_argumento->tipo_resultado
En la definición del cuerpo de la función la sintaxis es como sigue:
nombre_funcion nombre_argumento=<implementacion>
Ejemplo de un programa que calcula el doble de un número entero |
|
doble::Int->Int doble x=2*x |
En principio, la signatura puede omitirse de forma que el sistema haga inferencia sobre los tipos. Recordemos que estamos trabajando en un sistema de tipos fuerte.
En este lenguaje funcional se puede utilizar la sentencia condicional IF del modo siguiente:
if <expresion1> then <expresion2> else <expresion3>
Para realizar bucles y otros tipos de estructuras de control, usaremos el concepto de recurrencia.
Ejemplo de programas que comprueban si un número es, o no, par |
|
par::Int->Bool par x=(x ‘mod’ 2)==0 par::Int->Bool par x=if (x ‘mod’ 2)==0 then True else False |
Además en Haskell se pueden definir tipos generales, de la forma siguiente: identidad::a->a es más general que identidad::Int->Int ya que a es un tipo que abarca muchas más instancias que el tipo Int.
Por otro lado, ningún nombre de variable puede aparecer más de una vez en la parte izquierda de cada ecuación en la definición de una función. Por tanto, no podemos definir funciones del tipo:
iguales x x = True
iguales __=False
Podemos utilizar la siguiente notación cuando necesitemos que los argumentos de una función cumplan ciertas condiciones:
iguales x y
|x==y = True
|otherwise = False
En general esta notación se usa de la forma siguiente:
funcion x1 x2 ... xn
|condicion1 =e1
|condicion2 =e2
...
|condicionm =em
|otherwise =eotros
Operandos y operaciones
La Tabla 2 muestra el tipo de operaciones que se pueden realizar, el número de operandos que corresponden a cada operación y el operador que se utiliza en cada caso.
Operaciones entre números |
suma |
+ |
2 operandos en todas las operaciones de este tipo |
resta |
- |
||
multiplicacion |
* |
||
división entera |
‘div’ |
||
resto |
‘mod’ |
||
división |
/ |
||
Operaciones booleanas |
True, False |
True y False no se aplican sobre operandos. AND y OR se aplican sobre dos operandos y NOT sobre un operando |
|
AND |
&& |
||
OR |
|| |
||
NOT |
not |
||
Operaciones entre caracteres |
Obtener carácter |
chr |
Se aplican sobre un operando |
Obtener Código ASCII |
ord |
Aritmética y comparaciones
En el lenguaje Haskell tenemos un conjunto de operadores relacionales que se especifican a continuación, en la Tabla 3.
Tipo |
Descripción |
== |
Igual |
/= |
Distinto |
< |
Menor |
> |
Mayor |
<= |
Menor o igual |
>= |
Mayor o igual |
Podemos definir las funciones en Haskell de varios modos. Veamos detenidamente cada uno de ellos.
Composición de funciones. Función aplicable sobre un argumento que es otra función. Primero se evalúa la función del argumento y, posteriormente, con el resultado obtenido, la composición es evaluada.
Patter Machine. Las funciones se pueden definir de varios modos. Esto implica que la forma de evaluación de las mismas se hará mediante Patter Machine. El intérprete buscará de arriba abajo en el conjunto de definiciones de una función aquella que concuerde con la llamada especificada.
Currificación. Dada la función f x y, f se aplica sobre el primer argumento, es decir, sobre x resultando una función f’ que se aplica sobre el segundo argumento y.
El tipo de funciones que vamos a usar en Haskell es no estricto. Recordemos que una función no estricta es aquella que posee evaluación perezosa. Si aplicamos una función estricta sobre una expresión que falla o no termina, la función también falla. Con las funciones no estrictas, en cambio, no sucede dicho fallo. Por ejemplo, si tenemos la función
constante Int->Int
constante x=1
En un lenguaje estricto la llamada constante(1/0) fallaría, sin embargo, en un lenguaje no estricto devuelve 1.
Ejemplos
Para entender adecuadamente lo expuesto en los puntos anteriores, veamos algunos ejemplos interesantes de manejo de este lenguaje.
Ejemplos |
|
Ejemplo que ilustra la utilización de los tipos polimórficos o genéricos. identidad::a->a identidad x=x Ejemplo que ilustra la composición de funciones. doble Int->Int doble x=2*x par Int->Bool par x=(x ‘mod’ 2)==0 ?.-par doble 3 Ejemplo que ilustra la utilización del Pattern Machine. factorial Int->Int factorial 0=1 factorial x=x*factorial(x-1) |