Programación funcional Inferencia de tipos manual para

Anuncio
Programación funcional
Inferencia de tipos manual para programas Haskell
J. Sánchez Hernández
Enero de 2007
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
El sistema de tipos de Haskell
Es una variante del propuesto R. Milner en 1978 (con antecedentes
en J.R. Hindley, 1969).
• admite polimorfismo paramétrico
• toda expresión tiene un tipo principal, o bien no es tipable
• hay un algoritmo de inferencia para deducir el tipo principal
de cualquier expresión (o decidir que está al tipada).
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Tipo de una expresion
Se siguen los siguientes pasos:
1 Decorar de la forma “id :: <tipo>” cada identificador id (de
variable o de función) y cada resultado de una aplicación (bien
sea de función o de constructora) con una instancia fresca de
su tipo si se conoce o con una variable fresca a en caso
contrario, teniendo en cuenta que:
• las apariciones de una variable en la misma expresión han de
estar decoradas con el mismo tipo (puesto que deben tener el
mismo tipo)
• cada aparición de una función con tipo conocido ha de tener
una instancia fresca de su tipo (polimórfico).
• las tuplas (e1 , . . . , en ) pueden decorarse directamente con
(a1 , . . . , an )
2 Generar las ecuaciones según la siguiente regla:
e0 :: t0
e1 :: t1 . . . en :: tn (e0 e1 . . . en ) :: t
t0 = t1 → . . . t n → t
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Tipo para una regla de función
3 Resolver las ecuaciones generadas mediante un algoritmo de
unificación, teniendo en cuenta que:
• las tuplas y listas (y cualquier otra constructora) se
descomponen de manera ordinaria. Por ejemplo la ecuación
(t1 , t2 ) = (t10 , t20 ) se descompone en las dos ecuaciones
t1 = t10 , t2 = t20 y [t1 ] = [t2 ] se descompone en t1 = t2 .
• el constructor de tipo → se comporta como una constructora
mas, i.e., una ecuación de la forma t1 → t2 = t10 → t20 se
descompone en dos ecuaciones t1 = t10 , t2 = t20
Si el algoritmo tiene éxito proporciona el tipo principal para
la expresión (y cada una de sus subexpresiones). Si falla, la
expresión está mal tipada.
Nota: recordemos que la aplicación asocia por la izquierda y el
operador → por la derecha.
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Algoritmo de unificación de Martelli-Montanari
Aplicar alguna de las siguientes reglas mientras sea posible:
1. S ∪ {X = X }
S
2. S ∪ {f (t1 , . . . , tn ) = f (s1 , . . . , sn )}
S ∪ {t1 = s1 , . . . , tn = sn }
3. S ∪ {f (t1 , . . . , tn ) = g (s1 , . . . , sm )}
si f 6= g (ó n 6= m)
FALLO,
4. S ∪ {X = t}
S[X /t] ∪ {X = t},
si X 6∈ var (t), X ∈ var (S), X 6= t
5. S ∪ {X = t}
FALLO,
si X ∈ var (t)
6. S ∪ {t = X }
S ∪ {X = t},
si t 6∈ V
Este algoritmo proporciona u.m.g.’s idempotentes
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Ejemplo
Supongamos la expresión:
True == (False || (not False))
Sabemos que: (==) :: a → a → Bool y que not :: Bool → Bool,
(||) :: Bool → Bool → Bool.
Para facilitar las cosas ponemos la expresión en forma prefija
(==) True ((||) False (not False))
• Decoramos:
((==) :: a → a → Bool
True :: Bool
((||) :: Bool→Bool→Bool
False :: Bool
(not :: Bool→Bool False :: Bool) :: b
) :: c
) :: d
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
• Ecuaciones:
Bool → Bool = Bool → b
Bool → Bool → Bool = Bool → b → c
a → a → Bool = Bool → c → d
• Resolvemos: a = Bool, b = Bool, c = Bool, d = Bool
Otro ejemplo:
map (+1) [2]
Para facilitar las cosas utilizamos notación prefija en todos los
operadores y la lista [2] la escribimos de la forma (:) 2 [], sabiendo
que:
(:) :: a → [a] → [a]
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Inferencia de tipos para reglas de función
f p = |{z}
e
|{z}
cabeza
cuerpo
• Se decora la cabeza y el cuerpo, y se plantean las ecuaciones
del mismo modo, pero con una ecuación adicional entre el
tipo de la cabeza y el del cuerpo.
• Si hay guardas también se decoran y se plantean las
ecuaciones oportunas.
Ejemplo:
cua x = x ∗ x (suponiendo (∗) :: Int → Int → Int)
• Decoramos:
(cua::a x::b)::c = ((*)::Int→ Int→ Int
x::b x::b)::d
• Ecuaciones: {a = b → c, Int → Int → Int = b → b → d, c = d}
• Resolvemos: b = Int, c = Int, d = Int, a = Int → Int
(luego cua :: Int → Int)
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Tipo para funciones
• Se decora cada una de las reglas de función, anotando el
mismo tipo en todas las reglas para todas las apariciones
de la función que se está definiendo.
• Si el algoritmo de unificación tiene éxito el tipo obtenido para
la función es el tipo más general. Si no tiene éxito, la función
está mal tipada.
• Si la función tiene tipo declarado se comprueba que dicho tipo
sea una instancia del tipo inferido, i.e., que el tipo inferido es
más general que el declarado. Si es ası́, se anota el tipo
declarado para la función, en otro caso, error.
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Tipo para lambda-expresiones
λx → e
• se decoran x, e y la propia lambda expresión del modo
habitual, (λx :: a → edecorada :: b) :: c
• se plantean las ecuaciones resultantes de edecorada y una
ecuación adicional c = a → b
• se resuelve del modo ordinario
Las λ-expresiones con más de un argumento se reducen al caso de
un argumento. Por ejemplo: λx y → e) se decora como
(λx :: a1 → (λy :: a2 → edecorada :: a3 ) :: a4 ) :: a5
y se añaden las ecuaciones adicionales: a4 = a2 → a3 , a5 = a1 → a4
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Tipo para expresiones con let
let
<defs. locales>
...
in e 0
Las definiciones locales:
• se decoran del modo habitual, se plantean las ecuaciones y se
resuelven.
Para la expresión externa:
• Se decora e 0 utilizando los tipos inferidos en el paso anterior:
• el tipo de las variables globales queda congelado (no se toman
instancias frescas)
• para el resto de variables/funciones se toman instancias frescas
• Se plantean y resuelven las ecuaciones del modo habitual.
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Ejemplo
Sea h x y = (x, y ) con el tipo ya inferido h :: a → b → (a, b) y
f x = let
g y =hx y
in (x, g 3, g x, g True)
• Decoramos la definición local:
(g :: a1 y :: a2 ) :: a3 = (h :: a4 → a5 → (a4 , a5 ) x :: a6 y :: a2 ) :: a7
• Ecuaciones:
a1 = a2 → a3 ,
a4 → a5 → (a4 , a5 ) = a6 → a2 → a7 ,
• Resolvemos:
a1 = a2 → (a4 , a2 ),
a3 = (a4 , a2 ),
a6 = a4 ,
a5 → a2 ,
a3 = a7
a7 = (a4 , a2 )
Lo que nos interesa: g :: a2 → (a4 , a2 ) y x :: a4 , con a4 congelada
g :: a2 → (a4∗ , a2 )
J. Sánchez Hernández
x :: a4∗
Programación funcional
Inferencia de tipos manua
Ahora procedemos con la expresión externa:
• Cada aparición de g utiliza una instancia fresca del tipo
inferido
• x :: a4 es variable global (externa)
a4∗ queda congelado
• Decorando:
(f :: c0 x :: a4∗ ) :: c1 = (
x :: a4∗ ,
(g :: b1 → (a4∗ , b1 ) 3 :: Int) :: a8 ,
(g :: b3 → (a4∗ , b3 ) x :: a4∗ ) :: a9 ,
(g :: b5 → (a4∗ , b5 ) True :: Bool) :: a10
) :: (c2 , c3 , c4 , c5 )
• Resolviendo tenemos
f :: a4∗ → (a4∗ , (a4∗ , Int), (a4∗ , a4∗ ), (a4∗ , Bool))
Ejercicio: f 0 x = let g y = h x y in (fst (g 3) + fst (g x), g True)
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Tipo para programas
• En general, si una función f utiliza otra g en su definición el
tipo de g deberá inferirse antes que el tipo de f .
• ... pero puede haber funciones mútuamente recursivas, cuyo
tipo ha de inferirse simultáneamente.
Esto hace que el algoritmo sea algo más sofisticado de lo que
cabrı́a esperar:
• Construir el grafo de dependencias funcionales: los nodos
son los nombre de función; si f utiliza g en su definición
habrá un arco de f a g .
• Determinar las componentes fuertemente conexas del grafo
(que corresponden a definiciones mutuamente recursivas).
• Calcular la ordenación topológica de dichas componentes. Los
tipos se inferiran en dicho orden.
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Ejemplo
even 0 = True
even n = odd (n-1)
odd 0 = False
odd n = even (n-1)
Recursión mutua! Los tipos se infieren a la vez: se decoran todas
las reglas y se plantean las ecuaciones correspondientes.
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Inferencia de tipos con sistema de clases
El algoritmo es esencialmente el mismo, pero
• se anotan las cualificaciones/restricciones de tipo al decorar
las expresiones
• se propagan al resolver las ecuaciones. Además cuando una
misma variable está afectada por dos restricciones de clase se
conserva la más restriciva
• Eq a y Ord a se simplificarı́a a Ord a (todo tipo ordenado es
en particular un tipo con igualdad)
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Ejemplo
cua x = x ∗ x, sabiendo (∗) :: Num a ⇒ a → a → a
• Decoramos:
(cua :: a1 x :: a2 ) :: a3 =
((∗) :: Num a4 ⇒ a4 → a4 → a4
x :: a2
x :: a2 ) :: a3
• Ecuaciones + Restricciones
a1 = a2 → a3 ,
a4 → a4 → a4 = a2 → a2 → a3
Numa4
• Resolvemos:
a1 = a2 → a2 → a2 ,
a3 = a2 ,
a4 = a2
Num (a2 , a3 , a4 )
Tenemos cua :: Num a2 :: a2 → a2 → a2
J. Sánchez Hernández
Programación funcional
Inferencia de tipos manua
Descargar