Fecha de entrega: Mircoles 24 de Septiembre del 2014,
1. Expresiones, valores y tipos
Un programa en lenguaje funcional consiste en definir expresiones que computan (o denotan) valores. As como los valores, en el mundo real o matemtico, pertenecen a un conjunto, las expresiones pertenecen a un tipo.
Veamos que tipos pueden tener las expresiones de Haskell:
Tipos Bsicos como: Int, Char, Bool, etc. Funciones, como a Int, Bool (Bool Bool), etc.
Tuplas de cualquier longitud. Por ejemplo, (2 5 +1, 4 >0) es de tipo (Int, Bool).
Listas, secuencias ordenadas de elementos de un mismo tipo, con repeti- ciones. [Int] representa el tipo lista de enteros, [Bool] es una lista de booleanos, etc. Las expresiones de tipo lista se construyen con [] (que representa la lista vaca) y : (a:as es la lista que empieza con el elemento a y sigue con la lista as). Tambien pueden escribirse entre corchetes, con los elementos separados por comas:
El tipo String es sinnimo de [Char], y las listas de este tipo se pueden escribir entre comillas: "plp" es lo mismo que [p, l, p].
Tipos definidos por el usuario, con la clausula data. Los valores asociados a estos tipos consisten de un constructor (que se escribe con mayuscula) acompaado de 0 o ms argumentos.
data Dia = Lunes | Martes | Miercoles | Jueves | Viernes
Este tipo tiene cinco constructores, todos sin argumentos. A esta clase de tipos se los llama enumerados.
data Either a b = Left a | Right b data Maybe a = Nothing | Just a
Los tipos pueden tener argumentos, lo que los convierte en tipos para- metricos. Tipos como los de arriba suelen llamarse sumas o uniones, porque pueden representar la union de varios tipos. En particular, Either representa la union de dos tipos cualesquiera, y Maybe repre- senta el mismo conjunto que su argumento, ms un valor: Nothing. Left True :: Either True a Just 3 :: Maybe Int
data BinTree a = Nil | Branch a (BinTree a) (BinTree a)
Alumna: Molina Hernndez Elvira No. Control: 11210554 Fecha de entrega: Mircoles 24 de Septiembre del 2014,
Ac vemos que algunos de los constructores pueden tener como argumento el mismo tipo que determinan. Tipos as se suelen llamar tipos recursivos. En este caso, BinTree a representa el tipo de los arboles binarios cuyos nodos tienen un elemento de a. Nil :: BinTree a Branch True Nil (Branch (4 > 0) Nil Nil) :: BinTree Int
Las funciones sobre tipos construidos con la clusula data pueden de- finirse por pattern matching. Un patrn consiste de un constructor con tantas variables como argumentos tenga; al evaluar la funcin en un argumento, se intenta establecer una correspondencia entre l y cada patrn, reduciendo en la primera ecuacin donde se la encuentre.
prximo :: Dia Dia prximo Lunes = Martes prximo Martes = Miercoles proximo Miercoles = Jueves etc.
aInt :: (Either Bool Int) Int aInt (Left x) = if x then 1 else 0 aInt (Right x) = x
esVacio :: BinTree a b Bool esVacio Nil = True esVacio (Branch _ _ _) = False
Cuando las variables no se usan en el lado derecho de la ecuacin, se pueden reemplazar por un _.
Los tipos que permiten acceder a sus constructores y hacer pattern matching se llaman tipos algebraicos. Los booleanos, las tuplas y las listas tambien son tipos algebraicos! fst :: (a, b) a fst (x, y) = x
funciones que reciben multiples argumentos y devuelven un resultado suma :: (Int, Int) Int suma (x, y) = x + y
funciones que reciben un argumento y devuelven una funcin intermedia que completa el trabajo suma :: Int Int Int suma x y = x + y
En este ejemplo, suma x es una funcin que dado y devuelve x+y.
Alumna: Molina Hernndez Elvira No. Control: 11210554 Fecha de entrega: Mircoles 24 de Septiembre del 2014,
Esta correspondencia siempre existe, y en el segundo caso decimos que las funciones estan currificadas. La ventaja de las funciones currificadas es que permiten la aplicacion parcial. En una sola lnea estamos definiendo varias funciones! sucesor :: Int Int sucesor = suma 1
3. Polimorfismo y overloading
El sistema de tipos de Haskell permite definir funciones para ser usadas con ms de un tipo. Ya vimos algunos ejemplos: es Vacio, fst y length son funciones polimrficas. Otras funciones polimrficas tiles son:
flip :: (a b c) (b a c) flip f x y = f y x
(.) :: (a b) (c a) (c b) (.) f g x = f (g x)
Las funciones polimrficas en general se definen segun la estructura de sus argumentos, sin fijarse en que valores tienen internamente. Por ejemplo, la longitud de una lista puede calcularse sin saber nada acerca de sus elementos. Veamos ahora este otro ejemplo.
ejemplo 1: Definamos una funcin que devuelva verdadero cuando todos los elementos de una lista son iguales: todosIguales [] = True todosIguales [x] = True todosIguales (x:y:xs) = (x == y) && todosIguales (y:xs)
Qu tipo tiene esta funcin? En principio, vemos que puede tomar lis- tas de distintos tipos: todosIguales [1,2,3], todosIguales [True, True], todosIguales "hola" parecen expresiones vlidas. Sin embargo, por ejemplo, todosIguales [sucesor, suma 1] no se podra evaluar, porque las funciones no pueden compararse por igualdad. Lo que necesitamos es describir el conjunto de tipos que tienen la operacin ==, o ms en general, los tipos que tienen ciertas operaciones en particular. Para ello, Haskell provee las clases de tipos. En este caso, los que pueden compararse por igualdad corresponden a la clase Eq. todosIguales :: Eq a [a] Bool ? Otras clases utiles son:
Show: la clase de los tipos que pueden mostrarse por pantalla
Ord: la clase de los tipos que pueden compararse (por menor, igual, etc.)
Num: la clase de los tipos con operaciones aritmtica.
Alumna: Molina Hernndez Elvira No. Control: 11210554 Fecha de entrega: Mircoles 24 de Septiembre del 2014,
El mecanismo de clases se denomina overloading. Notemos que == no es una funcin polimrfica, por ms que pueda tomar argumentos de distintos tipos. Una funcin polimrfica tiene la misma definicin para cualquier tipo, y como dijimos, no podra explotar caractersticas particulares de cada uno. En cambio, una funcin sobrecargada, entre los distintos tipos, solo comparte el nombre (y la aridad): su definicin puede ser distinta para cada uno de ellos.
4. Alto orden
En Haskell, las funciones son valores como cualquier otro:
Pueden ser argumentos de una funcion
Pueden ser resultados de otras funciones
Pueden almacenarse en estructuras de datos
ejemplo 2: Definamos una funcin que toma el mximo de una lista: maximo :: Ord a [a] a maximo [x] = x maximo (x:y:xs) = if x > y then maximo (x:xs) else maximo (y:xs)
? Esta funcion es util siempre y cuando no nos interese otro orden que el del operador >. maximo [1,4,3] = 4 maximo ["abc", "a", "b"] = "b" maximo [False, True] = True
ejemplo 3: Ahora supongamos que quiero elegir, entre varias secuen- cias, la de mayor longitud. maxLongitud :: [[a]] [a] maxLongitud [xs] = xs maxLongitud (xs:ys:xss) = if length xs > length ys then maxLongitud (xs:xss) else maxLongitud (ys:xss)
? Esta funcion se parece mucho a la primera, y sin embargo, tuvimos que definirla aparte. Podremos generalizar maximo para que nos sirva en ambos casos? S: en lugar de tener (>) embebido en la definicion de la funcion, tomemos una funcion de comparacion como primer argumento! ejemplo 4: mejorSegun :: (a a Bool) [a] a mejorSegun _ [x] = x mejorSegun comp (x:y:xs) = if comp x y then mejorSegun comp (x:xs) else mejorSegun comp (y:xs)