Professional Documents
Culture Documents
1. Operadores
La figura 1 muestra la lista de algunos de los operadores básicos definidos en el preludio. La lista
está en orden de precedencia, esto es, los primeros elementos ejercen “mayor atracción” a sus operandos
que los últimos.
Operador Descripción
Aplicación de funciones
!! n–ésimo elemento de una lista
. Composición de funciones
**, ^, ^^ Exponenciación
*, /, ‘div‘, ‘quot‘, ‘mod‘, ‘rem‘ Multiplicación, división y residuo
+, - Suma y resta
:, ++ Inserción y concatenación en listas
==, \=, <, <=, >, >= Operadores relacionales
&& “and” booleano
|| “or” booleano
>>, >>= Composición secuencial de acciones
$ Aplicación
Conviene resaltar que los operadores encerrados entre un par de ‘‘’ son la versión infija de las
funciones con el mismo nombre2 . En Haskell podemos convertir cualquier función binaria a infija
encerrándola entre dos caracteres ‘‘’.
De manera similar en que podemos convertir funciones binarias a operadores infijos, también po-
demos convertir operadores infijos a funciones prefijas encerrándolos entre paréntesis. Por ejemplo
Prelude> (+) 10 10
20
Prelude> (&&) True False
False
f $ x = (f x)
Esta definición parece redundante, sin embargo, puede ser útil para evitar un exceso de paréntesis.
Por ejemplo, en lugar de escribir
i (h (g (f x)))
1 pedro.gongora@gmail.com
2 El caracter ‘`’ es el acento grave, diferente de ‘’’ y ‘´’, que son un simple apóstrofo y el acento agudo (el que usamos
en español).
1
podemos escribir
i $ h $ g $ f x
El operador $ también puede ayudar con funciones de orden superior. Por ejemplo
Los operadores >> y >>= son fundamentales para la entrada/salida en Haskell. Más adelante se
estudiarán.
Prelude> :t not
not :: Bool -> Bool
El resultado not :: Bool -> Bool, debe leerse como “not es una función que va de booleanos a
booleanos”. En matemáticas escribirı́amos
not : B → B
Prelude> :t (&&)
(&&) :: Bool -> Bool -> Bool
Hay que recordar que en Haskell, las funciones están “curryficadas”, por lo que la función && no es
una función que va de parejas de booleanos a booleanos. En realidad, && es una función que recibe un
valor booleano y devuleve una función que, a su vez, recibe otro booleano y regresa otro booleano.
Parece complejo, pero vayamos por partes. La aplicación de funciones se denota con un espacio,
por ejemplo, la expresión
f x
donde ((&&) True) es una función que devuelve True sólo si su operando también es True. De hecho,
si examinamos el tipo de ((&&) True), vermos lo siguiente:
2
Prelude> :t ((&&) True)
(True &&) :: Bool -> Bool
En Haskell también podemos crear funciones “normales” usando tuplas. Por ejemplo, defininamos
la función
Main> :t and2
and2 :: (Bool,Bool) -> Bool
Es conveniente resaltar que las tuplas, a diferencia de las listas, son heterogeneas, es decir, los
elementos de una tupla pueden ser de tipos distintos.
Prelude> :t length
length :: [a] -> Int
En este caso, a es una variable de tipos. Esto quiere decir que podemos sustituir a por cualquiera de
los tipos de datos disponibles. De esta manera podemos tener una sola función que calcule la longitud
de una lista de elementos de cualquier tipo.
2.3. Clases
Al revisar la figura 1, seguramente surge una pregunta: ¿por qué hay varias versiones para ope-
radores similares? Por ejemplo, intuitivamente, / y ‘div‘ parece que hacen lo mismo. En realidad,
trabajan con tipos de datos diferentes.
Prelude> :t (/)
(/) :: (Fractional a) => a -> a -> a
Prelude> :t (div)
(div) :: (Integral a) => a -> a -> a
En la firma de la función div, observamos que se refiere a un tipo de dato Integral a. Esto quiere
decir que a es una instancia de la clase Integral.
En Haskell los tipos de datos pueden derivarse de otros tipos de datos. Por ejemplo Int e Integer
son instancias de Integral (Integer nos sirve para representar enteros no acotados). La figura 2 tiene
algunas de las clases básicas definidas en el preludio.
Para obtener más información sobre algun tipo o clase, podemos usar el comando :i
3
Prelude> :i Int
-- type constructor
data Int
-- instances:
instance Eq Int
instance Ord Int
instance Num Int
instance Bounded Int
instance Real Int
instance Integral Int
instance Ix Int
instance Enum Int
instance Read Int
instance Show Int
Otros tipos de datos básicos son Char, Double, Float y Bool. El tipo [Char] es para listas de
caracteres, mientras que un tipo [a] es para listas de algún tipo a. String es un sinónimo de [Char].
sumList [] = 0
sumList (h:t) = h + sumList t
Si examinamos su tipo veremos que Haskell infiere, por el uso del operador +, que es una función
que trabaja con cualquier instancia de la clase Num.
Podemos restringir nuestra función a listas de enteros de la siguiente forma
Incluso, podemos ser más generales y restringirla a cualquier instancia de la clase Integral
4
3. Tipos algebráicos
3.1. Sinónimos
En Haskell podemos crear nuestros propios tipos de datos. Por ejemplo, el tipo String mencionado
anteriormente está creado de la siguiente forma:
La palabra type crea sinónimos para tipos de datos existentes. Podemos crear nombres más con-
venientes para los tipos disponibles
El carácter _ es un comodı́n o wildcard. En este contexto, significa que cualquier planeta, que no
sea la Tierra, no es habitable (recordemos que la concordancia o pattern–matching se hace en el orden
de como se escribieron los patrones).
Podemos hacer que Haskell construya automáticamente algunas funciones por nosotros indicándole
que derive de clases ya definidas. Por ejemplo,
Esta nueva definición de Planeta permite usar algunas funciones ya establecidas para las clases
Eq, Ord, Show y Enum.
5
3.3. Tipos producto
Con data también podemos crear tipos más complejos, por ejemplo
En este caso, EmpInterno y EmpExterno se llaman “constructores”. Podemos leer las definicio-
nes como “Un empleado es interno o externo”. “Un empleado interno tiene nombre y salario”. “Un
empleado externo tiene nombre y una empresa que lo contrata”.
En el caso de los planetas, Mercurio, etc., decimos que son constructores con aridad 0 (nullary
constructors).
Hay que tomar en cuenta que los nombres de los tipos de datos y los constructores, deben comenzar
con una letra mayúscula.
data AExp = Lit Int | Add AExp AExp | Mul AExp AExp
La definición de AExp tiene dos componentes. Lit Int es el “conjunto base”, es decir, el conjunto
de las literales enteras, mientras que a Add y Mul, podemos verlas como las “funciones constructoras”.
Ası́ como lo hacemos en matemáticas, podemos definir funciones recursivas para nuestro conjunto
inductivo.
Definimos una función eval para los elementos del conjunto base:
6
Ahora que nuestro tipo de datos tiene significado, podemos evaluar algunas expresiones:
Main> evalrec (Add (Lit 12) (Mul (Lit 34) (Lit 1)))
46
4. Ejercicios
1. Defina un tipo de dato recursivo para las expresiones booleanas con los siguientes elementos:
a) Las constantes booleanas V y F,
b) Variables de un carácter que representen a las proposiciones atómicas,
c) Los conectivos lógicos Not, And, Or, IfThen, Iff.
2. Defina una función de evaluación para los elementos del conjunto básico (para las proposiciones
atómicas sólo defina unos cuantos ejemplos).
3. Defina una función de evaluación recursiva para el resto de los elementos.