Tema 3: Tipos y clases
Índice
1. Conceptos básicos sobre tipos
1.1. ¿Qué es un tipo?
- Un tipo es una colección de valores relacionados.
- Un ejemplo de tipos es el de los valores booleanos:
Bool
- El tipo
Bool
tiene dos valoresTrue
(verdadero) yFalse
(falso). Que
v
es un valor del tipoT
(quev
tiene tipoT
) se representa porv :: T
Cálculo de tipo con
:type
λ> :type True True :: Bool λ> :type False False :: Bool
- El tipo
Bool -> Bool
está formado por todas las funciones cuyo argumento y valor son booleanos. Ejemplo de tipo
Bool -> Bool
λ> :type not not :: Bool -> Bool
1.2. Inferencia de tipos
Regla de inferencia de tipos:
f :: A -> B, e :: A ------------------- f e :: B
Tipos de expresiones:
λ> :type not True not True :: Bool λ> :type not False not False :: Bool λ> :type not (not False) not (not False) :: Bool
Error de tipo:
λ> :type not 3 Error: No instance for (Num Bool) λ> :type 1 + False Error: No instance for (Num Bool)
1.3. Ventajas de los tipos
- Los lenguajes en los que la inferencia de tipo precede a la evaluación se denominan de tipos seguros.
- Haskell es un lenguaje de tipos seguros.
- En los lenguajes de tipos seguros no ocurren errores de tipos durante la evaluación.
La inferencia de tipos no elimina todos los errores durante la evaluación. Por ejemplo,
λ> :type 1 `div` 0 1 `div` 0 :: (Integral t) => t λ> 1 `div` 0 *** Exception: divide by zero
2. Tipos básicos
Bool
(Valores lógicos):- Sus valores son
True
yFalse
.
- Sus valores son
Char
(Caracteres):- Ejemplos:
'a'
,'B'
,'3'
,'+'
- Ejemplos:
String
(Cadena de caracteres):- Ejemplos:
"abc"
,"1 + 2 = 3"
- Ejemplos:
Int
(Enteros de precisión fija):- Enteros entre \(-2^{31}\) y \(2^{31}-1\).
- Ejemplos:
123
,-12
Integer
(Enteros de precisión arbitraria):- Ejemplos:
1267650600228229401496703205376
.
- Ejemplos:
Float
(Reales de precisión arbitraria):- Ejemplos:
1.2
,-23.45
,45e-7
- Ejemplos:
Double
(Reales de precisión doble):- Ejemplos:
1.2
,-23.45
,45e-7
- Ejemplos:
3. Tipos compuestos
3.1. Tipos listas
- Una lista es una sucesión de elementos del mismo tipo.
[T]
es el tipo de las listas de elementos de tipoT
.Ejemplos de listas:
[False, True] :: [Bool] ['a','b','d'] :: [Char] ["uno","tres"] :: [String]
- Longitudes:
- La longitud de una lista es el número de elementos.
- La lista de longitud 0,
[]
, es la lista vacía. - Las listas de longitud 1 se llaman listas unitarias.
El tipo de una lista no informa sobre su longitud:
['a','b'] :: [Char] ['a','b','c'] :: [Char]
El tipo de los elementos de una lista puede ser cualquiera:
[['a','b'],['c']] :: [[Char]]
3.2. Tipos tuplas
- Una tupla es una sucesión de elementos.
(T1, T2, ..., Tn)
es el tipo de las n-tuplas cuya componente i-ésima es de tipoTi
.Ejemplos de tuplas:
(False,True) :: (Bool,Bool) (False,'a',True) :: (Bool,Char,Bool)
- Aridades:
- La aridad de una tupla es el número de componentes.
- La tupla de aridad 0,
()
, es la tupla vacía. - No están permitidas las tuplas de longitud 1.
El tipo de una tupla informa sobre su longitud:
('a','b') :: (Char,Char) ('a','b','c') :: (Char,Char,Char)
El tipo de los elementos de una tupla puede ser cualquiera:
(('a','b'),['c','d']) :: ((Char,Char),[Char])
3.3. Tipos de funciones
- Una función es una aplicación de valores de un tipo en valores de otro tipo.
T1 -> T1
es el tipo de las funciones que aplica valores del tipoT1
en valores del tipoT2
.Ejemplos de funciones:
not :: Bool -> Bool isDigit :: Char -> Bool
Ejemplo de función con múltiples argumentos:
suma (x,y)
es la suma dex
ey
. Por ejemplo,suma (2,3)
es5
. Su definición essuma :: (Int,Int) -> Int suma (x,y) = x+y
Ejemplo de función con múltiples valores:
deCeroA n
es la lista de los números desde0
hastan
. Por ejemplo,deCeroA 5
es[0,1,2,3,4,5]
. Su definición esdeCeroA :: Int -> [Int] deCeroA n = [0..n]
- Notas:
- En las definiciones se ha escrito la signatura de las funciones.
- No es obligatorio escribir la signatura de las funciones.
- Es conveniente escribir las signatura.
4. Parcialización
4.1. Parcialización
- Mecanismo de parcialización (currying en inglés): Las funciones de más de un argumento pueden interpretarse como funciones que toman un argumento y devuelven otra función con un argumento menos.
Ejemplo de parcialización:
suma' :: Int -> (Int -> Int) suma' x y = x+y
suma'
toma un enterox
y devuelve la funciónsuma' x
que toma un enteroy
y devuelve la suma dex
ey
. Por ejemplo,λ> :type suma' 2 suma' 2 :: Int -> Int λ> :type suma' 2 3 suma' 2 3 :: Int
Ejemplo de parcialización con tres argumentos:
mult :: Int -> (Int -> (Int -> Int)) mult x y z = x*y*z
mult
toma un enterox
y devuelve la funciónmult x
que toma un enteroy
y devuelve la funciónmult x y
que toma un enteroz
y devuelvex*y*z
. Por ejemplo,λ> :type mult 2 mult 2 :: Int -> (Int -> Int) λ> :type mult 2 3 mult 2 3 :: Int -> Int λ> :type mult 2 3 7 mult 2 3 7 :: Int
4.2. Aplicación parcial
- Las funciones que toman sus argumentos de uno en uno se llaman currificadas (curried en inglés).
- Las funciones
suma'
ymult
son currificadas. Las funciones currificadas pueden aplicarse parcialmente. Por ejemplo,
λ> (suma' 2) 3 5
Pueden definirse funciones usando aplicaciones parciales. Por ejemplo,
suc :: Int -> Int suc = suma' 1
suc x
es el sucesor dex
. Por ejemplo,suc 2
es3
.
4.3. Convenios para reducir paréntesis
- Convenio 1: Las flechas en los tipos se asocia por la derecha. Por ejemplo,
Int -> Int -> Int -> Int
representa aInt -> (Int -> (Int -> Int))
- Convenio 2: Las aplicaciones de funciones se asocia por la izquierda. Por
ejemplo,
mult x y z
representa a((mult x) y) z
- Nota: Todas las funciones con múltiples argumentos se definen en forma currificada, salvo que explícitamente se diga que los argumentos tienen que ser tuplas.
5. Polimorfismo y sobrecarga
5.1. Tipos polimórficos
- Un tipo es polimórfico ("tiene muchas formas") si contiene una variable de tipo.
- Una función es polimórfica si su tipo es polimórfico.
La función
length
es polimófica. Comprobación:λ> :type length length :: [a] -> Int
- Significa que que para cualquier tipo
a
,length
toma una lista de elementos de tipoa
y devuelve un entero. a
es una variable de tipos.- Las variables de tipos tienen que empezar por minúscula.
Ejemplos de polimorfismo de
length
:length [1, 4, 7, 1] == 4 length ["Lunes", "Martes", "Jueves"] == 3 length [reverse, tail] == 2
5.2. Ejemplos de funciones polimórficas
(fst p)
es la primera componente del parp
. Por ejemplo,fst (1,'x') == 1 fst (True,"Hoy") == True
Su tipo es
fst :: (a,b) -> a
(head xs) es el primer elemento de =xs
. Por ejemplo,head [2,1,4] == 2 head ['b','c'] == 'b'
Su tipo es
head :: [a] -> a
(take n xs) es la lista de los =n
primeros elementos de xs. Por ejemplo,take 3 [3,5,7,9,4] == [3,5,7] take 2 ['l','o','l','a'] == "lo" take 2 "lola" == "lo"
Su tipo es
take :: Int -> [a] -> [a]
(zip xs ys)
es la lista de pares de los elementos dexs
eys
en las mismas posiciones. Por ejemplo,zip [3,5] "lo" == [(3,'l'),(5,'o')]
Su tipo es
zip :: [a] -> [b] -> [(a, b)]
id
es la función identidad. Por ejemplo,id 3 == 3 id 'x' == 'x'
Su tipo es
id :: a -> a
5.3. Tipos sobrecargados
- Un tipo está sobrecargado si contiene una restricción de clases.
- Una función está sobrecargada si su tipo está sobrecargado.
La función
sum
está sobrecargada. Comprobación:λ> :type sum sum :: (Num a) => [a] -> a
- Significa que que para cualquier tipo numérico
a
,sum
toma una lista de elementos de tipoa
y devuelve un valor de tipoa
. Num a
es una restricción de clases.- Las restricciones de clases son expresiones de la forma
C a
, dondeC
es el nombre de una clase ya
es una variable de tipo. Ejemplos:
sum [2, 3, 5] == 10 sum [2.1, 3.23, 5.345] == 10.675
5.4. Ejemplos de tipos sobrecargados
Ejemplos de funciones sobrecargadas:
(-) :: (Num a) => a -> a -> a (*) :: (Num a) => a -> a -> a negate :: (Num a) => a -> a abs :: (Num a) => a -> a signum :: (Num a) => a -> a
Ejemplos de números sobrecargados:
5 :: (Num t) => t 5.2 :: (Fractional t) => t
6. Clases básicas
- Una clase es una colección de tipos junto con ciertas operaciones sobrecargadas llamadas métodos.
Clases básicas:
Clase Descripción Eq
tipos comparables por igualdad Ord
tipos ordenados Show
tipos mostrables Read
tipos legibles Num
tipos numéricos Integral
tipos enteros Fractional
tipos fraccionarios
6.1. La clase Eq
(tipos comparables por igualdad)
Eq
contiene los tipos cuyos valores con comparables por igualdad.Métodos:
(==) :: a -> a -> Bool (/=) :: a -> a -> Bool
- Instancias:
Bool
,Char
,String
,Int
,Integer
,Float
yDouble
.- tipos compuestos: listas y tuplas.
Ejemplos:
False == True == False False /= True == True 'a' == 'b' == False "aei" == "aei" == True [2,3] == [2,3,2] == False ('a',5) == ('a',5) == True
6.2. La clase Ord
(tipos ordenados)
Ord
es la subclase deEq
de tipos cuyos valores están ordenados.Métodos:
(<), (<=), (>), (>=) :: a -> a -> Bool min, max :: a -> a -> a
- Instancias:
Bool
,Char
,String
,Int
,Integer
,Float
yDouble
.- tipos compuestos: listas y tuplas.
Ejemplos:
False < True == True min 'a' 'b' == 'a' "elegante" < "elefante" == False [1,2,3] < [1,2] == False ('a',2) < ('a',1) == False ('a',2) < ('b',1) == True
6.3. La clase Show
(tipos mostrables)
Show
contiene los tipos cuyos valores se pueden convertir en cadenas de caracteres.Método:
show :: a -> String
- Instancias:
Bool
,Char
,String
,Int
,Integer
,Float
yDouble
.- tipos compuestos: listas y tuplas.
Ejemplos:
show False == "False" show 'a' == "'a'" show 123 == "123" show [1,2,3] == "[1,2,3]" show ('a',True) == "('a',True)"
6.4. La clase Read
(tipos legibles)
Read
contiene los tipos cuyos valores se pueden obtener a partir de cadenas de caracteres.Método:
read :: String -> a
- Instancias:
Bool
,Char
,String
,Int
,Integer
,Float
yDouble
.- tipos compuestos: listas y tuplas.
Ejemplos:
read "False" :: Bool == False read "'a'" :: Char == 'a' read "123" :: Int == 123 read "[1,2,3]" :: [Int] == [1,2,3] read "('a',True)" :: (Char,Bool) == ('a',True)
6.5. La clase Num
(tipos numéricos)
Num
es la subclase deEq
yShow
de tipos cuyos valores son númerosMétodos:
(+), (*), (-) :: a -> a -> a negate, abs, signum :: a -> a
- Instancias:
Int
,Integer
,Float
yDouble
. Ejemplos:
2+3 == 5 2.3+4.2 == 6.5 negate 2.7 == -2.7 abs (-5) == 5 signum (-5) == -1
6.6. La clase Integral
(tipos enteros)
Integral
es la subclase deNum
cuyo tipos tienen valores enteros.Métodos:
div :: a -> a -> a mod :: a -> a -> a
- Instancias:
Int
eInteger
. Ejemplos:
11 =div= 4 == 2 11 =mod= 4 == 3
6.7. La clase Fractional
(tipos fraccionarios)
Fractional
es la subclase deNum
cuyo tipos tienen valores no son enteros.Métodos:
(/) :: a -> a -> a recip :: a -> a
- Instancias:
Float
yDouble
. Ejemplos:
7.0 / 2.0 == 3.5 recip 0.2 == 5.0
7. Material complementario
El código del tema se encuentra en este enlace.
Este tema también se encuentra en los siguientes formatos:
- Como transparencias en PDF.
- Como libro interactivo en IHaskell sobre Jupyter.
- Como vídeo de clase.
8. Bibliografía
- R. Bird. Introducción a la programación funcional con Haskell. Prentice
Hall, 2000.
- Cap. 2: Tipos de datos simples.
- A. Gibiansky Typeclasses: Polymorphism in Haskell.
- G. Hutton. Programming in Haskell. Cambridge University Press, 2007.
- Cap. 3: Types and classes.
- B. O'Sullivan, D. Stewart y J. Goerzen. Real World Haskell. O'Reilly, 2008.
- Cap. 2: Types and Functions.
- B.C. Ruiz, F. Gutiérrez, P. Guerrero y J.E. Gallardo. Razonando con
Haskell. Thompson, 2004.
- Cap. 2: Introducción a Haskell.
- Cap. 5: El sistema de clases de Haskell.
- S. Thompson. Haskell: The Craft of Functional Programming, Second
Edition. Addison-Wesley, 1999.
- Cap. 3: Basic types and definitions.