La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

Chequeo e inferencia de tipos

Presentaciones similares


Presentación del tema: "Chequeo e inferencia de tipos"— Transcripción de la presentación:

1 Chequeo e inferencia de tipos
clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres) mecanismo indispensable para la construcción de software confiable Lenguajes y sistemas de tipos: Hindley - Milner chequeo e inferencia automáticos lenguajes fuerte y estáticamente tipados

2 Chequeo e inferencia de tipos (2)
Formalizaremos la noción de expresión correctamente tipada Mostraremos cómo puede ser construído un algoritmo de inferencia de tipos para poder deducir el tipo de una expresión (si lo tiene) La descripción del algoritmo posibilitará ilustrar la derivación de un algoritmo complejo. Un buen ejemplo de una presentación puramente funcional de un programa que maneja estados.

3 Sistemas de tipos Un sistema de tipos consiste de un conjunto de reglas que establecen los pasos de deducción o inferencias que pueden ser aplicados cuando se intenta demostrar que una cierta expresión e tiene un cierto tipo T. Un sistema tal, además, nos brinda un marco formal para poder razonar sobre todas las posibles inferencias válidas determinadas por las reglas que componen al sistema. Por ejemplo, una de las reglas que veremos establece que: si podemos probar que la expresión b tiene tipo Bool y la expresión e tiene tipo T y la expresión d tiene tipo T entonces la expresión if b then e else d tiene tipo T

4 Sistemas de tipos (2) La regla anterior puede ser usada para probar que una expresión tiene un tipo particular. Puede también ser incluída en un algoritmo diseñado para chequear tipos automáticamente. Alternativamente, una adaptación de esta regla podría ser incluída en un algoritmo para inferir tipos de expresiones. Si tal algoritmo tuviera que inferir el tipo de una expresión if b then e else d podría proceder así: inferir primero el tipo de las tres sub expresiones, por ejemplo, b : T1, e : T2 y d : T3 luego chequear que T1 es equivalente al tipo Bool y que T2 y T3 son tipos equivalentes En un sistema polimórfico “es equivalente a” debe entenderse como “tiene una instancia en común con” en vez de “es idéntico a” como es el caso en un sistema monomórfico.

5 Forma de juicio En las reglas del sistema la afirmación de que una expresión tiene un cierto tipo será relativizada con respecto a una colección de hipótesis acerca de los tipos de las variables y constructores que ocurren en la expresión en cuestión. Esta colección de hipótesis es usualmente llamado contexto. Nosotros usaremos  para denotar contextos y las reglas de formación son las siguientes:  := [] | , x:T donde x es una variable y T un tipo La forma de juicio utilizada en las reglas será  |- e : T, que debe ser leído como “la expresión e tiene tipo T en el contexto  ” La regla informalmente descripta previamente puede ser formulada ahora de la siguiente forma:

6 Forma de juicio (2)  |- b : Bool  |- e : T  |- d : T  |- if b then e else d : Bool En esta regla es claro que el contexto tiene que ser el mismo en cada una de las premisas, pero ya veremos que éste no será el caso en general. La colección de hipótesis puede ser definida como: 1) un mapeo finito del conjunto de variables y constructores en tipos 2) un conjunto de pares (x,T) tal que no existen dos pares (x,T) y (x,T´) tal que T  T´. 3) una lista de pares (x,T). En caso de ambigüedad , la primer ocurrencia de un par (x,T) oculta las restantes. Esta última es la que usaremos.

7 Un sistema de tipos monomórfico
Comenzaremos presentando un sistema monomórfico de tipos. El lenguaje de tipos es el siguiente: T := Int | Bool | T -> T | T x T Un poco de notación: , ’ denotará concatenación de hipótesis [x:T] denotará la hipótesis simple de que x tiene tipo T usaremos (x:T) en  para expresar el hecho de que T es el tipo asociado a x en el contexto .

8 (var)  |- x : T si x : T está en 
(con)  |- c : T si c : T está en   |- f : T -> T’  |- e : T (ap)  |- f e : T’  |- a : T  |- b : T’ (prod)  |- (a,b) : T x T’  |- b : Bool  |- e : T  |- d : T (if)  |- if b then e else d : Bool  |- e’ : T’ ,x:T’ |- e : T (let)  |- let x = e’ in e : T , x:T’ |- e : T (abs)  |- \ x -> e : T’ -> T

9 Un sistema de tipos monomórfico
Notar que: no hemos considerado otros patterns que no sean variables en expresiones let. las abstracciones son restringidas al caso simple de una variable ligada. las reglas (var) y (con) son de hecho axiomas que indican que se puede inferir que variables y constructores son objetos del tipo que tienen asociado en el contexto. en las reglas (abs) y (let) se efectúa descarga de hipótesis. que en (let) la expresión ligada a la variable x tiene que ser del mismo tipo que aquél asociado a la variable en la segunda premisa.

10 Ejemplos de derivaciones
1) [] |- \ x -> x : int -> int 2) [3:int] |- let f = \ x -> x in f 3 : int A continuación discutiremos la introducción de variables de tipos, lo que en un principio parecerá requerir la necesidad de un procedimiento más inteligente para la construcción de pruebas.

11 Polimorfismo Una aproximación ingenua a polimorfismo sería introducir variables de tipos y permitir inferencias de la forma  |- e : T (cuando T´es una instancia de T)  |- e : T’ Diríamos, por ejemplo, que si a es una variable de tipo, entonces int es una instancia de a, int -> b es una instancia de a, siendo b una variable de tipo, etc. Sin embargo, una regla de esta forma podría introducir inconsistencias en las derivaciones si es usada en forma irrestringida. Para ver , entre otros, este punto, empezaremos por analizar una metodología para asignarle tipos a expresiones.

12 Cómo derivar tipos Presentaremos la estructura de una expresión como un árbol. Las hojas serán variables y constantes, las que desplegaremos en el tope y a medida que “bajamos” hacia la raíz atravesaremos nodes etiquetados con los constructores de expresión correspondientes. Ejemplo: \x y z -> x z (y z) x z y z @ @ @ \x y z ->

13 Cómo derivar tipos (2) Cada nodo en el árbol anterior corresponde a una subexpresión de la expresión original, y debería por lo tanto poseer un tipo. Si asignamos las variables de tipo T0, T1, ..., T7 obtenemos un árbol similar x : T0 z : T y : T z : T3 @ T T5 T6 ----- \ x y z -> T7 Para estar seguros que una expresión (e1 e2) es bien tipada, la expresión e1 debe tener un tipo funcional a -> b, donde e2 tiene tipo a y entonces (e1 e2) tiene tipo b.

14 Cómo derivar tipos (2) Entonces ya podemos inferir ciertas restricciones: T0 = T1 -> T4 T2 = T3 -> T5 T4 = T5 -> T6 Sustituyendo en el árbol nos queda ahora x : T1 -> T5 -> T6 z : T y : T3 -> T z : T3 @ T5 -> T T5 T6 ----- \ x y z -> T7 Es claro ahora que T7 será de la forma (T1 -> T5 -> T6 ) -> (T1 -> T5) -> ... Pero qué hacer con las etiquetas T1 y T3 de z?

15 Cómo derivar tipos (3) Deben ser todas las ocurrencias de una variable ligada por una abstracción asignadas el mismo tipo? Analizaremos el tipado de la siguiente función, asumiendo que el tipo debe ser el mismo: 1) F = \ f a b c -> c (f a) (f b) Qué pasa con F I 0 ‘a’ ? Y con F ord 0 ´a’ K ? Obs: Para que una expresión sea bien tipada, no es suficiente que no pueda “go wrong” cuando es considerada ella sola o en un contexto favorable. Tenemos que asegurarnos que este es al caso cuando es considerada en todo contexto

16 Polimorfismo (2) Resumiendo, hasta ahora hemos adoptado las siguientes reglas: i) La parte funcional f de una aplicación (f a) tiene un tipo a -> b, donde a es el tipo del argumento a y b es el tipo de la expresión (f a). ii) Todas las ocurrencias de una variable ligada por una abstracción deben ser consideradas del mismo tipo. También hemos usado implicitamente la regla siguiente: si ( T1 -> T2) = ( T3 -> T4) entonces T1 = T2 y T3 = T4 Necesitamos un mecanismo que permita introducir polimorfismo pero que a su vez controle cuándo instancias de tipos pueden ser usadas y cuándo no.

17 Polimorfismo (3) Un análisis similar de expresiones let x = e´ in e muestra que no sería peligroso usar instancias del tipo de e´ cuando derivamos el tipo de e. En este caso el tipo de la expresión e´, que debe ser usado como el tipo de x, establece un único tipo T1 tal que todas las ocurrencias de x pueden en forma segura tomar como tipo instancias del tipo T1. Por ejemplo, si tratamos de derivar un tipo para le expresión let x = e´ in (succ x, not x) bajo las mismas hipótesis, entonces e´ tendría que ser un objeto de tipo a (y no una instancia de a)

18 Tipando expresiones let
Consideremos ahora la siguiente expresión: let S = \ x y z -> x z (y z) K = \ x y -> x in S K K Parece razonable permitir que K tome tipos diferentes en sus distintas ocurrencias en la expresión S K K. S : T6 -> T7 -> T K : T6 T7 -> T K : T7 S : TS K : TK T8 let S,K. T9

19 Tipando expresiones let (2)
Sabemos que: TS = (T0 -> T1 -> T2) -> (T0 -> T1) -> T0 -> T2 TK = T3 -> T4 -> T3 Las nuevas restricciones a considerar son como relacionar T8 y T9 y los tipos TS y TK con sus ocurrencias en el cuerpo de la expresión let. 1) T8 = T9 2) Como proceder en el segundo caso Obtener una expresión totalmente evaluada para TS y TK Introducir nuevas etiquetas de tipos para las ya existentes en TS y TK T6 -> T7 -> T8 = (T10 -> T11 -> T12) -> (T10 -> T11) -> T10 -> T12 T6 = T13 -> T14 -> T13 T7 = T15 -> T16 -> T15

20 Tipando expresiones let (3)
De la primer ecuación podemos derivar que T6 = T10 -> T11 -> T12 T7 = T10 -> T11 T8 = T10 -> T12 y entonces T10 = T13 = T12 T11 = T14 T10 = T15 T11 = T16 -> T15 lo que permite expresar los tipos de las ocurrencias de K como T6 = T10 -> (T16 -> T10) -> T10 T7 = T10 -> T16 -> T10 y finalmente T9 = T8 = T10 -> T10

21 Tipando expresiones let (3)
La regla que podría ser adoptada para expresiones de esta forma es que los tipos de las ocurrencias de las abreviaciones en el cuerpo de la expresión deben ser instancias de los tipos de las correspondientes definiciones. El procedimiento usado para para computar esas instancias es instanciar las variables en los tipos de las expresiones abreviadas con nuevas variables, elaborando una instancia fresca para cada ocurrencia en el cuerpo del let del nombre definido. De hecho, ya veremos que no todas esas variables podrán ser instanciadas.

22 Polimorfismo (3) Por lo visto anteriormente entonces, para efectuar el chequeo de tipos no consideraremos a la expresión let x = e´ in e como equivalente a la expresión (\ x -> e) e’ aún cuando semánticamente lo son una vez que han sido tipadas. En la primer expresión el tipo de e´ nos brinda la información necesaria para poder determinar si la expresión entera puede ser tipada. En la segunda expresión, en cambio, primero tenemos que inferir un tipo para la abstracción sin poder usar la información de que e´ tiene un cierto tipo.

23 Variables genéricas y esquemas de tipos
Trataremos tipos polimórficos de variables introducidas en abstracciones de forma diferente a aquellas introducidas en definiciones locales. De esta forma obtendremos un mecanismo seguro de polimorfismo. Si una variable x es introducida en una abstracción \ x -> e, todas las ocurrencias de x en e deben ser consideradas como del mismo tipo. Las variables de tipos que ocurren en el tipo asignado a x son llamadas variables no genéricas, y no podrán ser instanciadas. Por otro lado, si x es introducida en una definición local let x = e´ in e, entonces podremos ser más flexibles y permitir que a distintas ocurrencias de x en e se le asignen distintas instancias del tipo polimórfico asignado a e´ y x.

24 Esquemas de tipos Para formalizar la distinción descripta anteriormente introduciremos una nueva noción, la de esquema de tipo. Esquemas de tipos tendrán la forma a1 a2 ... an . T, donde T es un tipo polimórfico, posiblemente incluyendo las variables a1 ... an, pero sin cuantificadores internos. Formalmente, T ::= a | Int | Bool | T -> T | T x T S ::= a . S | T Usaremos S para denotar esquemas de tipos. Notar que tipos son un caso particular de esquema, no cuantificadores. Esquemas de tipos son también conocidos como shallow types.

25 Esquemas de tipos (2) Por ejemplo, el tipo del operador id (= \ x -> x) es más precisamente capturado por el esquema a. a -> a, en vez de sólo el tipo a -> a. Durante el proceso de inferencia usaremos formas cuantificadas y no cuantificadas, pero nunca tipos que no son shallow, como (a. a -> a) -> (a. a -> a) Este tipo no es shallow porque aparece un cuantificador en una subexpresión del tipo. Aunque esta forma de tipos podría ser incluída en un sistema de tipos, no lo serán en nuestro sistema ya que no se conoce un algoritmo automático de inferencia para esta forma de expresiones de tipos.

26 Especialización Para especializar un esquema se podrá sustituir variables cuantificadas por tipos particulares. Por ejemplo, a. a -> a, puede ser especializado sustituyendo a por Int en la expresión a -> a, para obtener la instancia Int -> Int. Sin embargo esta especialización será restringida por la condición de ser shallow. Por ejemplo, si queremos instanciar a. a -> a con un esquema para a como b. b -> Int, entonces no podremos sustituir y obtener el siguiente tipo no shallow (b. b -> Int) -> (b. b -> Int) En cambio tendremos que formar la siguiente (menos general) instancia b. (b -> Int) -> (b -> Int) Esta idea es formalizada con la noción de instancia genérica.

27 Instancia genérica Def.
Si S y S´ son esquemas de tipos, diremos que S´ es una instancia genérica, lo que escribiremos S  S´, si S´ es obtenido a partir de S removiendo los cuantificadores, sustituyendo tipos por algunas o todas las variables (previamente) cuantificadas y luego agregando cuantificadores para todas las variables que ocurran en la expresión obtenido, excepto por aquellas que ya ocurrían libres en el esquema S. La última restricción significa que a.( b -> a)  g.(b -> (b x g)) pero no a.( b -> a)  bg.(b -> (b x g))

28 Instancia genérica (2) Luego especificaremos: definición de  FV (S)
Notar que si T y T´ son tipos entonces T  T´ sii T  T´ En el sistema de inferencia permitiremos que generalizar un tipo T a un esquema a.T, pero esto será restringido al caso en que a no ocurre libre en el contexto. Se da el caso de que variables de tipos no genéricas pueden ser identificadas con las variables libres en un contexto

29 Un sistema de inferencia de tipos polimórfico
Las diferencias introducidas por el polimorfismo respecto al sistema monomórfico son: las reglas (var) y (con) permiten que lo que sea asociado a una variable o un constructor sea un esquema de tipo y no solamente un tipo. la regla (let) es similarmente modificada, a la variable de ligadura se le puede asociar también un esquema. Sin embargo (abs) se mantiene igual. dos nuevas reglas, (gen) e (inst), son introducidas, las que permiten que un tipo sea generalizado e instanciado con un tipo particular, respectivamente.

30 (var)  |- x : S si x : S está en 
(con)  |- c : S si c : S está en   |- f : T -> T’  |- e : T (ap)  |- e : S  |- f e : T’ (abs) (gen) a  FV(G)  |- e : a.S  |- a : T  |- b : T’ (prod)  |- (a,b) : T x T’  |- b : Bool  |- e : T  |- d : T (if)  |- if b then e else d : Bool  |- e’ : S ,x:S |- e : T  |- e : S (let) (inst) S  S´  |- let x = e’ in e : T  |- e : S´ , x:T’ |- e : T (abs)  |- \ x -> e : T’ -> T

31 Reglas para recursión y definiciones simultáneas
La extensión para que la regla (let) considere varias definiciones es directa:  |- e1 : S  |- en : Sn ,x1:S1 ... xn:Sn |- e : T (let n)  |- let x1 = e xn = en in e : T Sin embargo, la extensión para definiciones recursivas no es tan simple. En la expresión letrec f = (...f...) in (...f...f...) sería tambien razonable que f pudiera tomar distintos tipos en el cuerpo de la expresión. Sin embargo ahora hay un nuevo problema a considerar: la variable introducida por una definición recursiva puede también ocurrir en la parte derecha de la definición. En general, cuando hay varias definiciones mutuamente recursivas como en

32 Regla para definiciones simultáneas
letrec x1 = (...x1...xi...xj...) ... xn = (...xn...xi...xj...) in (...x1...xi...xj...xn...) cualquiera de las xi puede ocurrir muchas veces tanto en la parte derecha de las definiciones como en el cuerpo del let. Que criterio adoptar ? Ocurrencias de nombres definidos en las partes derechas deben tomar exactamente el mismo o tipo o instancias ? Al igual que con las abstracciones necesitamos restringir los tipos de las variables a tipos ordinarios en vez de a esquemas de tipos (las variables de los tipos deben ser no genéricas).

33 Regla para definiciones simultáneas
Sin emabargo, esta restricción no tiene por qué aplicarse a las ocurrencias de los nombres definidos en el cuerpo de la expresión. Entonces, obtenemos la siguiente regla:  , ´ |- e1 : S  , ´ |- en : Sn , ´´ |- e : T (letrec n)  |- letrec x1 = e xn = en in e : T donde: ´ = [x1 : T1, ... , xn : Tn] ´ = [x1 : S1, ... , xn : Sn] y Si  Ti ,, tal que las variables ligadas en Si no pertenecen a FV(  ), con i = 1..n

34 Ejemplos de inferencia
1) [] |- \ x -> x : a . a -> a 2) si G = [5:Int , true : Bool], probar que G |- let f = \ x -> x in (f true, f 5) : Bool x Int Como ilustración de la diferencia entre variables genéricas y no genéricas nosotros afirmamos que el siguiente juicio no puede ser probado en nuestro sistema: G |- (\ f -> (f true, f 5)) (\ x -> x) : T para cualquier tipo T y G con la misma definición que arriba. (queda como ejercicio ver qué es lo que sucede)


Descargar ppt "Chequeo e inferencia de tipos"

Presentaciones similares


Anuncios Google