1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad...

34
1 Chequeo e inferencia de tipos 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

Transcript of 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad...

Page 1: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

1

Chequeo e inferencia de tipos

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

Page 2: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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.

Page 3: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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

Page 4: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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.

Page 5: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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:

Page 6: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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 tipos2) 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.

Page 7: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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 .

Page 8: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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

Page 9: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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.

Page 10: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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.

Page 11: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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 es una variable de tipo, entonces int es una instancia de , int -> es una instancia de , siendo 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.

Page 12: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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 ->

Page 13: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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 : T1 y : T2 z : T3

----------------- @ ------------------ @ T4 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 donde e2 tiene tipo y entonces (e1 e2) tiene tipo

Page 14: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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 : T1 y : T3 -> T5 z : T3

------------------------------ @ -------------------------- @ T5 -> T6 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?

Page 15: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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

Page 16: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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

donde es el tipo del argumento a y 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.

Page 17: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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)

Page 18: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

18

Tipando expresiones let

Consideremos ahora la siguiente expresión:

let S = \ x y z -> x z (y z) K = \ x y -> xin 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 -> T8 K : T6

------------------------------------ @

T7 -> T8 K : T7

------------------------ @ S : TS K : TK T8

------------------------------------------------------ let S,K. T9

Page 19: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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

Page 20: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

20

Tipando expresiones let (3)

De la primer ecuación podemos derivar que T6 = T10 -> T11 -> T12 T7 = T10 -> T11

T8 = T10 -> T12

y entoncesT10 = T13 = T12

T11 = T14

T10 = T15

T11 = T16 -> T15

lo que permite expresar los tipos de las ocurrencias de K comoT6 = T10 -> (T16 -> T10) -> T10

T7 = T10 -> T16 -> T10

y finalmenteT9 = T8 = T10 -> T10

Page 21: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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.

Page 22: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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.

Page 23: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

23

Variables genéricas y esquemas de tiposTrataremos 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.

Page 24: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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 Tdonde T es un tipo polimórfico, posiblemente incluyendo las variables pero sin cuantificadores internos.

Formalmente, T ::= | Int | Bool | T -> T | T x T S ::= 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.

Page 25: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

25

Esquemas de tipos (2)

Por ejemplo, el tipo del operador id (= \ x -> x) es más precisamente capturado por el esquema en vez de sólo el tipo

Durante el proceso de inferencia usaremos formas cuantificadas y no cuantificadas, pero nunca tipos que no son shallow, como ((

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.

Page 26: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

26

EspecializaciónPara especializar un esquema se podrá sustituir variables cuantificadas por tipos particulares. Por ejemplo, , puede ser especializado sustituyendo por Int en la expresión 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 con un esquema paracomoInt, entonces no podremos sustituir y obtener el siguiente tipo no shallow

(Int) -> (Int)

En cambio tendremos que formar la siguiente (menos general) instancia Int) -> Int)

Esta idea es formalizada con la noción de instancia genérica.

Page 27: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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 quexpero nox

Page 28: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

28

Instancia genérica (2)

Luego especificaremos:• definición de • FV (S)• FV ()

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 pero esto será restringido al caso en que 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

Page 29: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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.

Page 30: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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)FV|- e : .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

Page 31: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

31

Reglas para recursión y definiciones simultáneas

La extensión para que la regla (let) considere varias definiciones es directa:|- e1 : S1 ... |- en : Sn x1:S1 ... xn:Sn|- e : T(let n)|- let x1 = e1 ... 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

Page 32: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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).

Page 33: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

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 : S1 ... ´|- en : Sn ´´|- e : Tletrec n)|- letrec x1 = e1 ... 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

Page 34: 1 Chequeo e inferencia de tipos Tipos: clasificación de valores de acuerdo a su uso. La necesidad de tipos: protección contra errores (variables libres)

34

Ejemplos de inferencia

1) [] |- \ x -> x :

siInt , true : Bool], probar que

|- 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:

|- (\ f -> (f true, f 5)) (\ x -> x) : T

para cualquier tipo T y con la misma definición que arriba (queda como ejercicio ver qué es lo que sucede)