Tema IV

Anuncio
Capı́tulo 4. Definiciones de tipo
40
Definiciones de tipo
Sinónimos de tipo
• Un sinónimo de tipo renombra un tipo existente.
X El nuevo nombre y el antiguo son totalmente intercambiables.
type Entero =Integer
type DeEnteroEnEntero =Entero → Entero
uno :: Entero
uno = 1
sucesor :: DeEnteroEnEntero
sucesor x = x + 1
type ParFlotantes = (Float, Float)
parCeros :: ParFlotantes
parCeros = (0.0, 0.0)
X Un sinónimo de tipo muy usado, definido en Prelude, es String:
type String = [Char ]
nombre :: String
nombre = [ 0 p 0 , 0 e 0 , 0 p 0 , 0 e 0 ]
Main> length nombre
4 :: Int16 :: Int
nombreYApellidos :: String
nombreYApellidos = ”José E. Gallardo”
Main> length nombreYApellidos
16 :: Int
Capı́tulo 4. Definiciones de tipo
Definiciones de tipos de datos
• El programador puede definir nuevos tipos.
Tipos enumerados
data Dı́aSemana = Lunes | Martes | Miércoles | Jueves
| Viernes | Sábado | Domingo
deriving Show
unDı́a :: Dı́aSemana
unDı́a = Lunes
laborables :: [Dı́aSemana]
laborables = [Lunes, Martes, Miércoles, Jueves, Viernes]
• Dı́aSemana es un constructor de tipo
• Lunes, Martes, . . . son constructores de datos
X Un constructor de dato también puede ser
simbólico, y en ese caso debe ser un operador
que comience por el carácter dos puntos :
X Un mismo constructor de dato no puede aparecer en dos tipos distintos en un mismo ámbito.
41
• Haskell permite usar los constructores de
datos como patrones:
esFinSemana
esFinSemana Sábado
esFinSemana Domingo
esFinSemana
:: Dı́aSemana → Bool
= True
= True
= False
data Dirección = Norte | Sur | Este | Oeste
deriving Show
girar 90
girar 90 Norte
girar 90 Sur
girar 90 Este
girar 90 Oeste
:: Dirección → Dirección
= Este
= Oeste
= Sur
= Norte
data ColorSemáforo = Rojo | Verde | Amarillo
deriving Show
En Prelude
data Bool = False | True
deriving (Eq, Ord , Ix , Enum, Read , Show , Bounded )
Capı́tulo 4. Definiciones de tipo
42
Uniones
• Podemos definir nuevos tipos uniendo dos tipos
existentes:
data LetraOEntero = Letra Char | Entero Integer
deriving Show
unValor :: LetraOEntero
unValor = Letra 0 x 0
otroValor :: LetraOEntero
otroValor = Entero 15
listaMixta :: [LetraOEntero]
listaMixta = [ Letra 0 a 0 , Entero 10, Entero 12, Letra 0 b 0 ]
• Cada constructor de dato tiene un tipo.
Letra :: Char → LetraOEntero
Entero :: Integer → LetraOEntero
incLoE
:: LetraOEntero → LetraOEntero
incLoE (Entero n) = Entero . (+1) $ n
incLoE (Letra c) = Letra . chr . (+1) . ord $ c
Main> incLoE (Letra 0 a 0 )
Letra 0 b 0 :: LetraOEntero
Main> incLoE (Entero 10)
Entero 11 :: LetraOEntero
Es incorrecto:
data LetraOEntero = Char | Integer – – Incorrecto!!!
deriving Show
• Temperaturas expresadas en dos escalas:
data Temp = Centı́grado Float | Fahrenheit Float
deriving Show
estáCongelado
:: Temp → Bool
estáCongelado (Centı́grado grados) = grados ≤ 0.0
estáCongelado (Fahrenheit grados) = grados ≤ 32.0
Capı́tulo 4. Definiciones de tipo
43
Productos
• También es posible definir tipos con un único
constructor y varias componentes:
data Racional = Par Integer Integer deriving Show
X El constructor en este caso tiene el tipo
• Nombre, apellidos y edad de una persona:
type Nombre
type Apellido1
type Apellido2
type Edad
data Persona
Par :: Integer → Integer → Racional
unMedio :: Racional
unMedio = Par 1 2
• Usando patrones podemos definir operaciones
para trabajar con racionales:
numerador
:: Racional → Integer
numerador (Par x ) = x
denominador
denominador (Par
:: Racional → Integer
y) = y
por :: Racional → Racional → Racional
(Par a b) ‘por ‘ (Par c d ) = Par (a ∗ c) (b ∗ d )
Main> (Par 1 2) ‘por ‘ (Par 1 3)
Par 1 6 :: Racional
=String
=String
=String
=Integer
=UnaPersona Nombre
Apellido1
Apellido2
Edad
deriving Show
pepe :: Persona
pepe = UnaPersona ”José” ”Pérez” ”Sánchez” 18
• Haskell permite nombrar las distintas componentes de un tipo:
type Nombre
type Apellido
type Edad
data Persona
=String
=String
=Integer
=UnaPersona {
nombre :: Nombre,
apellido1 :: Apellido,
apellido2 :: Apellido,
edad
:: Edad
}
deriving Show
Capı́tulo 4. Definiciones de tipo
• Con registros, los datos se pueden declarar con
la sintaxis habitual o nombrando los campos:
pepe :: Persona
pepe = UnaPersona ”José” ”Pérez” ”Sánchez” 18
juan :: Persona
juan = UnaPersona {
nombre = ”Juan”,
apellido1 = ”López”,
apellido2 = ”Giménez”,
edad
=24
}
X Además, se introducen automáticamente
funciones selectoras para las componentes:
Main> : t edad
edad :: Persona → Edad
Main> edad juan
24 :: Integer
44
• Se puede construir un nuevo valor a partir de
otro existente, indicando el valor de las componentes que defieren:
Main> juan {apellido1 = ”Boj”, apellido2 = ”Sanz”}
UnaPersona {nombre = ”Juan”, apellido1 = ”Boj”,
apellido2 = ”Sanz”, edad = 24} :: Persona
• Lo cual, por supuesto, no afecta para nada al
valor de juan:
Main> juan
UnaPersona {nombre = ”Juan”, apellido1 = ”López”,
apellido2 = ”Giménez”, edad = 24} :: Persona
• La siguiente función devuelve una nueva Persona
con la edad incrementada:
cumpleaños :: Persona → Persona
cumpleaños p = p {edad = edad p + 1}
Main> cumpleaños juan
UnaPersona{nombre = ”Juan”, apellido1 = ”López”,
apellido2 = ”Giménez”, edad = 25} :: Persona
Capı́tulo 4. Definiciones de tipo
45
• Los identificadores de constructores binarios de
dato también pueden ser simbólicos
X Siempre han de comenzar por el carácter dos
puntos
data Complejo =Float : − Float deriving Show
origen :: Complejo
origen = 0.0 : − 0.0
parteReal
parteReal (x : −
:: Complejo → Float
) = x
X El constructor de dato introducido tiene el siguiente tipo:
(: −) :: Float → Float → Complejo
Main>
1.0 : −
Main>
1.0 : −
(: −) 1.0 2.0
2.0 :: Complejo
1.0 : − 2.0
2.0 :: Complejo
• Un constructor literal puede usarse de forma
infija si se escribe entre acentos franceses:
dosTercios :: Racional
dosTercios = 2 ‘Par ‘ 3
• También es posible definir registros variantes
• Un tipo que representa cuatro clases de figuras:
type Radio =Float
type Lado =Float
data Figura = Cı́rculo Radio
| Cuadrado Lado
| Rectángulo Lado Lado
| Punto
deriving Show
unCı́rculo :: Figura
unCı́rculo = Cı́rculo 25.0
unRectángulo :: Figura
unRectángulo = Rectángulo 10.0 15.0
listaFiguras :: [Figura]
listaFiguras = [Cı́rculo 15.0,
Cuadrado 3.0,
Rectángulo 5.0 6.0]
• Define dos funciones para calcular el área y
perı́metro de una Figura.
Capı́tulo 4. Definiciones de tipo
46
Tipos recursivos
• Haskell permite la definición de tipos infinitos mediante declaraciones de tipos recursivos.
undefined
undefined
| False
indefinidoN
indefinidoN
:: a
= undefined
:: Nat
= undefined
– – siempre falla
Los naturales
I El cero es un número natural.
I Si n es un número natural, entonces n + 1
también lo es.
En Haskell la declaración es:
data Nat = Cero | Suc Nat deriving Show
Cero :: Nat
Suc :: Nat → Nat
• Algunas funciones
uno
uno
dos
dos
:: Nat
= Suc Cero
:: Nat
= Suc (Suc Cero)
– – puede escribirse Suc uno
• Cero, Suc Cero, Suc (Suc Cero), . . . los llamaremos valores definidos.
• Existen otros valores del tipo que no representan ningún número natural.
Main> uno
Suc Cero :: Nat
Main> dos
Suc (Suc Cero) :: Nat
Main> indefinidoN
Program error : undefined
X Este valor suele denotarse semánticamente
como ⊥ (se lee bottom).
X Se considera que las evaluaciones que producen algún error o que no terminan (divergen)
se reducen a ⊥.
indefinidoN =⇒ ⊥ .
Main> Suc indefinidoN
Suc
Program error : undefined
Main> Suc (Suc indefinidoN )
Suc (Suc
Program error : undefined
Capı́tulo 4. Definiciones de tipo
• Obsérvese que dan lugar a evaluaciones distintas. Ası́,
Suc indefinidoN =⇒ Suc ⊥
mientras que
Suc (Suc indefinidoN ) =⇒ Suc (Suc ⊥)
Suc indefinidoN está algo más definido que
indefinidoN .
esCero
:: Nat → Bool
esCero Cero = True
= False
esCero
Main> esCero indefinidoN
Program error : undefined
Main> esCero (Suc indefinidoN )
False :: Bool
infinitoN :: Nat
infinitoN = Suc infinitoN
Main> infinitoN
Suc (Suc (Suc (Suc (Suc (Suc (Suc (Suc {Interrupted !}
47
• La siguiente función comprueba si un valor de
tipo Nat representa un número par:
esPar
:: Nat → Bool
esPar Cero
= True
esPar (Suc x ) = not (esPar x )
• El siguiente operador calcula la suma de dos
valores del tipo Nat, analizando la forma del segundo argumento:
infixl 6 <+>
(<+>)
:: Nat → Nat → Nat
m <+> Cero
=m
m <+> (Suc n) = Suc (m <+> n)
uno <+> dos
=⇒ ! definición de dos
uno <+> Suc (Suc Cero)
=⇒ ! segunda ecuación de (<+>)
Suc (uno <+> (Suc Cero))
=⇒ ! segunda ecuación de (<+>)
Suc (Suc (uno <+> Cero))
=⇒ ! primera ecuación de (<+>)
Suc (Suc (uno))
=⇒ ! definición de uno
Suc (Suc (Suc Cero))
Capı́tulo 4. Definiciones de tipo
• En general, si una función tiene más de un
argumento recursivo, podemos intentar realizar
la recursión sobre uno de ellos.
infixl 6 <+>
(<+>)
:: Nat → Nat → Nat
Cero
<+> n = n
(Suc m) <+> n = Suc (m <+> n)
X No siempre es posible realizar la definición sobre cualquiera de los argumentos.
• Para el tipo Nat también es posible definir el
producto y la potencia:
infixl 7 <∗>
(<∗>)
:: Nat → Nat → Nat
m <∗> Cero
= Cero
m <∗> (Suc n) = m <∗> n <+> m
– – ya que m ∗ (n + 1) = m ∗ n + m
infixr 8 <↑>
(<↑>)
:: Nat → Nat → Nat
b <↑> Cero
= Suc Cero
b <↑> (Suc n) = b <∗> b <↑> n
– – ya que b ↑ (n + 1) = b ∗ b ↑ n
48
• Expresiones aritméticas simples sobre enteros:
data Expr = Valor Integer
| Expr :+: Expr
| Expr :−: Expr
| Expr :∗: Expr
deriving Show
Calcular el número de operadores que forman parte de una expresión:
numOpers
numOpers
numOpers
numOpers
numOpers
:: Expr → Integer
=0
(Valor )
(e1 :+: e2) = numOpers e1 + 1 + numOpers e2
(e1 :−: e2) = numOpers e1 + 1 + numOpers e2
(e1 :∗: e2) = numOpers e1 + 1 + numOpers e2
• Define una función que calcule el valor de una
expresión.
• Define una función que calcule cuántas constantes enteras aparecen en una expresión.
• Define una función que calcule el nivel de anidamiento máximo de un operador en una expresión.
Capı́tulo 4. Definiciones de tipo
Funciones de plegado
• Para cada tipo recursivo es posible definir un
recursor de plegado.
• Muchas de las funciones definidas sobre el tipo
Nat siguen un mismo patrón recursivo. Recordemos algunas:
data Nat
esPar
esPar Cero
esPar (Suc n)
= Cero | Suc Nat deriving Show
:: Nat → Bool
= True
= not (esPar n)
(<+>)
:: Nat → Nat → Nat
(<+>) m Cero
=m
(<+>) m (Suc n) = Suc (m <+> n)
X Siguen la plantilla:
fun
:: Nat → a
fun Cero
= e
fun (Suc n) = f (fun n)
X Función de plegado para el tipo Nat
foldNat
:: (a → a) → a → Nat → a
foldNat f e Cero
=e
foldNat f e (Suc n) = f (foldNat f e n)
49
• Entonces:
esPar = foldNat not True
(<+>) m =foldNat Suc m
Comportamiento de foldNat
..
f oldN at h z (Suc (Suc (i .veces
(Suc Cero)))) =⇒ h (h (i .veces
. . h z))
esPar (Suc (Suc Cero))
=⇒ ! por definición de esPar
foldNat not True (Suc (Suc Cero))
=⇒ ! por segunda ecuación de foldNat
...
True
• Escribe una función de plegado para el tipo
Expr
data Expr = Valor Integer
| Expr :+: Expr
| Expr :−: Expr
| Expr :∗: Expr
deriving Show
Capı́tulo 4. Definiciones de tipo
50
Tipos Parametrizados (o Polimórficos)
• Algunos tipos predefinidos (como las listas) son
polimórficos.
X Otro ejemplo:
data Par a = UnPar a a deriving Show
(a es una variable de tipo).
Main> UnPar 1 2
UnPar 1 2 :: Par Integer
Main> UnPar True False
UnPar True False :: Par Bool
Main> UnPar 0 a 0 True
ERROR
∗ ∗ ∗ Expression
∗ ∗ ∗ Term
∗ ∗ ∗ Type
∗ ∗ ∗ Does not match
:
:
:
:
:
Type error in application
UnPar 0 a 0 True
0 0
a
Char
Bool
X El tipo Either
data Either a b = Left a | Right b
deriving (Eq, Ord , Read , Show )
listaMixta :: [Either Char Integer ]
listaMixta = [Left 0 a 0 , Left 0 c 0 , Right 3, Left 0 z 0 ]
otra :: [Either Bool Char ]
otra = [Left True, Right 0 a 0 , Right 0 c 0 ]
X El tipo Maybe
data Maybe a = Nothing | Just a
deriving (Eq, Ord , Read , Show )
recı́proco :: Float → Maybe Float
recı́proco 0 = Nothing
recı́proco x = Just (1/x )
Main> recı́proco 0.001
Just 1000.0 :: Maybe Float
Main> recı́proco 0
Nothing :: Maybe Float
Capı́tulo 4. Definiciones de tipo
51
Definiciones newtype
• La declaración newtype crea un nuevo tipo isomorfo (con los mismos valores) a uno existente, pero
con una identidad propia para el sistema de tipos.
newtype Natural = UnNatural Integer deriving Show
• Todo lo dicho para data vale para newtype.
X newtype evita el nivel extra de indirección (causado por la evaluación perezosa) que la declaración
data introducirı́a.
• Las declaraciones newtype usan la misma sintaxis que las declaraciones data que poseen un único
constructor con un único campo.
Capı́tulo 4. Definiciones de tipo
Propiedades de funciones
• Podemos entender mejor el significado de un
programa estableciendo propiedades de las funciones que lo forman.
X Algunas propiedades pueden probarse directamente a partir de la definiciones de las funciones
involucradas.
• Demostrar que la composición de funciones es
asociativa
(.) :: (b → c) → (a → b) → (a → c)
g . f = λ x → g (f x )
Asociatividad de (.)
∀ v ¦ ((f1 . f2) . f3) v = (f1 . (f2 . f3)) v
((f1 . f2 ) . f3 ) v
≡! por definición de (.)
(f1 . f2 ) (f3 v )
≡! por definición de (.)
f1 (f2 (f3 v ))
Transformamos ahora la parte derecha:
(f1 . (f2 . f3 )) v
≡! por definición de (.)
52
f1 ((f2 . f3 ) v )
≡! por definición de (.)
f1 (f2 (f3 v ))
con lo cual queda demostrada la proposición original.
• Para tipos enumerados podemos probar la propiedad en cada caso
not
:: Bool → Bool
not True = False
not False = True
Doble negación
∀ x :: Bool ¦ not (not x ) = x
Si x es True, hay que probar not (not True) = True.
not (not True)
≡! por definición de not
not False
≡! por definición de not
True
Si x es False, . . .
Capı́tulo 4. Definiciones de tipo
53
• Las declaraciones de tipo recursivas dan lugar a conjuntos infinitos.
• La inducción es la técnica ideal para probar propiedades de funciones que actúan sobre tipos recursivos.
• El principio de inducción para el tipo Nat:
data Nat = Cero | Suc Nat deriving Show
Principio de inducción para valores definidos del tipo Nat

 P (Cero)
∀x :: Nat ¦ P (x ) ⇔ ∧

∀x :: N at ¦ P (x) ⇒ P (Suc x)
P Cero (demostrado en caso base)
⇒ ! por paso inductivo
P (Suc Cero)
⇒ ! por paso inductivo
P (Suc (Suc Cero))
¡¡las dos condiciones del principio de inducción son suficientes!!
X Este principio de inducción no es válido para valores del tipo Nat parcialmente definidos (como
indefinidoN e infinitoN ).
Capı́tulo 4. Definiciones de tipo
• Para el operador suma,
(<+>)
:: Nat → Nat → Nat
m <+> Cero
=m
m <+> (Suc n) = Suc (m <+> n)
Cero es neutro por la izquierda:
∀ x :: Nat ¦ Cero <+> x = x
Por el principio de inducción:
Caso Base: Cero <+> Cero = Cero
Paso Inductivo:
∀ x :: Nat ¦
Cero <+> x = x – – HI
⇒
Cero <+> (Suc x ) = Suc x
I Caso base:
Cero <+> Cero
≡! por definición de (<+>)
Cero
I Paso inductivo:
Cero <+> (Suc x )
54
≡! por definición de (<+>)
Suc (Cero <+> x )
≡! por hipótesis de inducción
Suc x
• Cuando en la proposición considerada aparece
más de una variable se puede realizar la inducción
sobre alguna de ellas.
Asociatividad de (<+>)
∀ x , y, z :: Nat ¦
(x <+> y) <+> z = x <+> (y <+> z )
Realizando la inducción sobre la variable z :
∀ z :: Nat ¦ P (z )
donde
P (z ) = ∀ x , y :: Nat ¦
(x <+> y) <+> z = x <+> (y <+> z )
Por el principio de inducción basta con probar:
Caso Base: P (Cero)
Paso Inductivo: ∀ z :: Nat ¦ P (z ) ⇒ P (Suc z )
• ¿Principio de inducción para el tipo Expr
(49)?
Capı́tulo 4. Definiciones de tipo
55
Sobrecarga
• Haskell permite definir funciones monomórficas y polimórficas.
X Un ejemplo de función monomórfica:
not
:: Bool → Bool
not True = False
not False = True
(sólo tiene sentido para booleanos).
X Un ejemplo de función polimórfica:
id :: a → a
id x = x
Prelude> id True
True :: Bool
– – id con tipo Bool → Bool
Prelude> (id not) True
False :: Bool
– – id con tipo (Bool → Bool ) → (Bool → Bool )
• Una función polimórfica tiene sentido para distintos usos.
• Función sobrecargada: a medio camino entre
las funciones monomórficas y las polimórficas.
X Tienen sentido para más de un tipo, pero
con restricciones.
• Si el tipo del operador (+) fuera:
(+) :: a → a → a
permitirı́a 1 + 2, 1.5 + 2.5 y True + False.
• Haskell utiliza un sistema de clases para modelar la sobrecarga.
• Una clase es un conjunto de tipos para los que
tiene sentido un grupo de funciones.
X Existen distintas clases predefinidas y cada
una contiene una serie de (tipos) ejemplares.
• La clase Num incluye los tipos Int, Integer ,
Double y Float.
X El modo de restringir el polimorfismo es vı́a
un contexto:
(+) :: Num a ⇒ a → a → a
Descargar