26
1 Derivando un algoritmo de inferencia de tipos El sistema de inferencia polimórfico no es syntax-directed. Las reglas (inst) y (gen) pueden ser aplicadas en cualquier paso de la derivación. Sin embargo, es posible restringir el uso de: (inst) inmediatamente después de las reglas (var) y (con) (gen) inmediatamente antes de (let) para obtener los esquemas más generales para las variables definidas. A continuación presentaremos un sistema modificado que fuerza las restricciones arriba discutidas, y que no incluye las reglas y .

Derivando un algoritmo de inferencia de tipos

  • Upload
    pepper

  • View
    43

  • Download
    0

Embed Size (px)

DESCRIPTION

Derivando un algoritmo de inferencia de tipos. El sistema de inferencia polim ó rfico no es syntax-directed. Las reglas (inst) y (gen) pueden ser aplicadas en cualquier paso de la derivación. Sin embargo, es posible restringir el uso de: - PowerPoint PPT Presentation

Citation preview

Page 1: Derivando un algoritmo de inferencia de tipos

1

Derivando un algoritmo de inferencia de tipos

El sistema de inferencia polimórfico no es syntax-directed. Las reglas (inst) y (gen) pueden ser aplicadas en cualquier paso de la derivación.

Sin embargo, es posible restringir el uso de:• (inst) inmediatamente después de las reglas (var) y (con)• (gen) inmediatamente antes de (let) para obtener los esquemas más generales para las variables definidas.

A continuación presentaremos un sistema modificado que fuerza las restricciones arriba discutidas, y que no incluye las reglas (inst) y (gen).Def. Esquema más generalGen(,T) = nT donde {n= FV(T) - FV()

Page 2: Derivando un algoritmo de inferencia de tipos

2

(var) |- x : T si x : S está en y S T

(con) |- c : T si c : S está en y S T|- 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 : T

|- e’ : T’ x:S|- e : T (let)donde S = Gen(,T’)|- let x = e’ in e : T x:T’|- e : T(abs)|- \ x -> e : T’ -> T

Page 3: Derivando un algoritmo de inferencia de tipos

3

Representación de expresiones de tipos

Para construír el algoritmo necesitaremos representar expresiones de tipos como objetos de un tipo Haskell:

type TVname = [Int]

data Texp = Tvar TVname | Tcons String [Texp] deriving(Eq,Read,Show)

-- some typesarrow t1 t2 = Tcons "arrow" [t1,t2]int = Tcons "int" []bool = Tcons ”bool" []cross t1 t2 = Tcons "cross" [t1,t2]list t = Tcons "list" [t]

Page 4: Derivando un algoritmo de inferencia de tipos

4

La mónada []

instance Monad [] where return a = [a] l >= f = concat (map f l)

bar :: Eq a => [a]->[a]->[a]bar l b = [ x lx notElem b ]

-- the type variables in a type

tvars_in (Tvar x) = return xtvars_in (Tcons name args) = args >= tvars_in

Page 5: Derivando un algoritmo de inferencia de tipos

5

Resolviendo ecuaciones

Consideremos el chequeo de una expresión (Ap e1 e2), donde hemos derivado el tipo t1 para e1 y el tipo t2 para e2. Entonces tendríamos que resolver la siguiente ecuación:

t1 = t2 -> (Tvar n)

donde n es un nombre de variable de tipo que no ha sido previamente usado. Como ya hemos visto en el chequeo de una expresión se generan restricciones, las que pueden ser entendidas como ecuaciones entre (esquemas) de tipos. Soluciones a estas ecuaciones pueden ser entendidas (e implementadas) como sustituciones de variables (de tipos) por expresiones de tipos.Entonces:

-- substitutionstype Subst = TVname -> Texp

Page 6: Derivando un algoritmo de inferencia de tipos

6

SustitucionesDada una sustitución phi y una expresión de tipo te, definimos a (sub_type phi te) como la expresión obtenida a partir de aplicar la sustitución phi a todas las variables de tipo en te:

sub_type :: Subst -> Texp -> Texp

sub_type phi (Tvar x) = phi xsub_type phi (Tcons name args) = Tcons name (map (sub_type phi) args)

Dos sustituciones pueden ser compuestas para definir otra sustitución:

subst_comp :: Subst -> Subst -> Subst subst_comp phi psi x = sub_type phi (psi x)

Propiedad: sub_type (subst_comp phi psi) = (sub_type phi) . (sub_type psi)

Page 7: Derivando un algoritmo de inferencia de tipos

7

Sustituciones (2)

La sustitución identidad

id_subst :: Substid_subst x = Tvar x

Una sustitución delta es aquella que sólo afecta a una variable

delta :: TVname -> Texp -> Substdelta x t y = if y == x then t else Tvar y

En general una sustitución puede asociarle a una variable un valor que a su vez tambien contiene variables. Si esas variables a su vez forman parte del dominio de la sustitución, entonces la misma no esta “completamente resuelta”. Un proceso iterativo podría ser necesario (problema de terminación si hay circularidad).

Page 8: Derivando un algoritmo de inferencia de tipos

8

Sustituciones (3)En general, estaremos interesados en obtener sustituciones normalizadas. La siguiente definición captura esta noción

Def.Una sustitución phi es idempotente si se cumple que (sub_type phi) . (sub_type phi) = sub_type phi

Def.Una expresión de tipo t es un punto fijo de una sustitución phi si sub_type phi t = t

En particular, si (Tvar x) es un punto fijo de phi, diremos que x no es afectada por phi.

Notar que si phi es idempotente, y phi afecta a tvn, entonces sub_type phi (Var tvn) es un punto fijo de phi

Page 9: Derivando un algoritmo de inferencia de tipos

9

Unificación

Ahora mostraremos cómo construír una sustitución que resuelva un conjunto dado de ecuaciones de tipos, usando unificación.

Un sistema de ecuaciones puede ser representado como una lista de pares de expresiones de tipos. Para resolver las ecuaciones, debemos hallar una sustitución phi que unifique las partes izquierdas y derechas de todas las ecuaciones en el sistema, donde decimos que phi unifica al par (t1, t2) si se cumple sub_type phi t1 = sub_type phi t2

Def.Una sustitución phi es no menos general que una sustitución psi si existe rho tal que psi = rho `subst_comp` phi

Page 10: Derivando un algoritmo de inferencia de tipos

10

Unificación (2)

El problema de unificación es el de hallar un unificador maximalmente general e idempotente de un conjunto de pares de expresiones [Robinson 65].

Será conveniente para la construcción del algoritmo considerar el problema de extender una sustitución que resuelve un conjunto de ecuaciones a una que resuelve una extension de ese conjunto.

Formularemos el problema de la siguiente forma:Dado un par (t1,t2) de expresiones de tipos, y una sustitución idempotente phi, nuestro algoritmo debería retornar un error si no existe una extensión de phi que unifica (t1,t2), y si no retornar (Ok psi) donde psi es un unificador idempotente de (t1,t2) que extiende a phi.

Page 11: Derivando un algoritmo de inferencia de tipos

11

Unificación (3)La ecuación más simple a resolver es una de la forma Tvar tvn = tPara manejar estos casos haremos uso de la siguiente función

extend :: Subst -> TVname -> Texp -> E Substextend phi x t = if t == Tvar x then return phi else if elem x (tvars_in t) then errorE “occur check” else return (subst_comp (delta x t) phi)

Una expresión (extend phi tvn t) será evaluada sólo cuando:i) phi es una sustitución idempotenteii) t es un punto fijo de phiiii) tvn no es afectada por phi (tvn no tiene un valor bajo phi)

Page 12: Derivando un algoritmo de inferencia de tipos

12

Unificación (4)unify :: Subst -> (Texp,Texp) -> E Substunify phi (Tvar x,t) = if phix == Tvar x then extend phi x t else unify phi (phix,phit) where phix = phi x phit = sub_type phi t

unify phi (Tcons n args,Tvar x) = unify phi (Tvar x,Tcons n args)

unify phi (Tcons n1 args1 ,Tcons n2 args2) = if n1 == n2 then unifyl (return phi) (zip args1 args2) else errorE “Different type constructors”

unifyl :: E Subst -> [(Texp,Texp)] -> E Substunifyl = foldr (\eq -> \ms -> ms >= (\s -> unify s eq))

Page 13: Derivando un algoritmo de inferencia de tipos

13

Esquemas de tipos y genericidad

Representaremos esquemas de tipos como objetos del siguiente tipo Haskell data TypeScheme = Scheme [TVname] Texp

Una variable de tipo que ocurre en un esquema (Scheme scvs t) es genérica si su nombre es un elemento de la lista scvs, en caso contrario será una variable no-genérica

nonGenericOfScheme :: TypeScheme -> [TVname]nonGenericOfScheme (Scheme l t) = bar (tvars_in t) l

Aplicando una sustitución a un esquema de tipo sin afectar variables genéricassub_scheme :: Subst -> TypeScheme -> TypeSchemesub_scheme phi (Scheme l t) = Scheme l (sub_type (exclude phi l) t) where exclude phi l x = if (elem x l) then (Tvar x) else (phi x)

Page 14: Derivando un algoritmo de inferencia de tipos

14

Listas de asociaciónCómo reflejar el hecho de que a cada variable libre en una expresión le asociaremos un esquema de tipo?Requerimientos sobre le estructura de datos:i) debe proveer un mapeo de variables libres a esquemasii) tendríamos que ser capaces de determinar cuál es el recorrido de ese mapping

Consideremos el chequeo de (let x = e in e´)

1) derivamos un tipo T para e en un contexto x1 : S1 ... xn : Sn, lo que equivale a construir una sustitución phi para las ecuaciones implicadas por la estructura de e tal que e : T en el contexto x1 : S1´ ... xn : Sn´, donde Si´ es la imagen de Si bajo phi.

2) Formamos el esquema S que asociaremos a x en el contexto, tal que las variables genéricas de S son aquellas de T excepto las no-genéricas que ocurran en S1´ ... Sn´.

Page 15: Derivando un algoritmo de inferencia de tipos

15

Listas de asociación (2)

Entonces cualquiera sea la estructura elegida para representar contextos nos deberá permitir acceso al conjunto de variables no-genéricas de su recorrido.

type AssocList a b = [(a,b)]

dom :: AssocList a b -> [a]val :: Eq a => AssocList a b -> a -> binstall :: AssocList a b -> a -> b -> AssocList a b

-- the range of an association list is the list of -- values (as obtained with val)

rng :: Eq a => AssocList a b -> [b]rng al = map (val al) (dom al)

Page 16: Derivando un algoritmo de inferencia de tipos

16

Contextos

type Context = AssocList Vname TypeScheme

nonGenericOfCtx :: Context -> [TVname]nonGenericOfCtx gamma = (rng gamma) >= nonGenericOfScheme

sub_ctx :: Subst -> Context -> Contextsub_ctx phi gamma = gamma >= (\(x,st) -> [(x,(sub_scheme phi st))])

Variables Frescas

type NameSupply = TVname

name_sequence :: NameSupply -> [TVname]

Page 17: Derivando un algoritmo de inferencia de tipos

17

Inferencia de tiposFinalmente, ahora estamos en condiciones de definir el algoritmo de inferencia. Este será una función (tc gamma ns e), donde

i) gamma es un contexto. Cuando el chequeador es invocado debe estar inicializado con los tipos de los identificadores predefinidos.ii) ns provee nombres frescos de variables de tiposiii) e es la expresión a ser chequeada

El valor retornado será un objeto de la mónada E, el que en caso de éxito retornará un par (phi,t), donde

i) phi es una sustitución definida sobre las variables no-genéricas de gammaii) t es un tipo derivado para la expresión e, en el contexto (sub_ctx phi gamma). Será un punto fijo de phi.

Page 18: Derivando un algoritmo de inferencia de tipos

18

Inferencia de tipos (2)

Definiremos la función tc por inducción en la estructura de las expresiones, con una clausula diferente por cada una de ellas:

tc :: Context -> NameSupply -> Exp -> E (Subst,Texp)

tc gamma ns (Var x) = tcvar gamma ns xtc gamma ns (Ap e1 e2) = tcap gamma ns e1 e2tc gamma ns (Lambda x e) = tclambda gamma ns x etc gamma ns (Let decls e) = tclet gamma ns (map fst decls) (map snd decls) etc gamma ns (LetRec decls e) = tcletrec gamma ns (map fst decls) (map snd decls) e

Page 19: Derivando un algoritmo de inferencia de tipos

19

Funciones auxiliaresadd_decls :: Context -> NameSupply -> [Vname] -> [Texp] -> Contextadd_decls gamma ns xs ts = (zip xs schemes)++gamma where schemes = map (genbar nongeneric ns) ts nongeneric = nonGenericOfCtx gamma genbar u n t = Scheme (map snd al) t' where al = zip scvs (name_sequence n) scvs = bar (nub (tvars_in t)) u t' = sub_type (al2subst al) t

tcl :: Context -> NameSupply -> [Vexp] -> E(Subst,[Texp])tcl gamma ns [] = return(id_subst,[])tcl gamma ns (e:es) = do (phi,t) <- tc gamma ns1 e (psi,ts) <- tcl (sub_ctx phi gamma) ns0 es return (subst_comp psi phi,(sub_type psit):ts))) where (ns0,ns1) = split ns

Page 20: Derivando un algoritmo de inferencia de tipos

20

Chequeando variablesCuando chequeamos una variable x en un cierto contexto gamma, con nombres ns, miramos el esquema de tipos asociado a la variable en gamma. Retornamos una nueva instancia del esquema donde las variables genéricas han sido reemplazadas por variables frescas

tcvar :: Context -> NameSupply -> Vname -> E(Subst,Texp)tcvar gamma ns x = return(id_subst,new_instance ns (val gamma x)) new_instance :: NameSupply -> TypeScheme -> Texpnew_instance ns (Scheme scvs t) = sub_type phi t where phi = al2subst (zip scvs (name_sequence ns)) al2subst al x = if (elem x (dom al)) then Tvar (val al x) else Tvar x

Page 21: Derivando un algoritmo de inferencia de tipos

21

Chequeando aplicacionesCuando chequeamos una aplicación (Ap e1 e2), primero construímos una sutitución phi que resuelva las restricciones sobre e1 y e2. Supongamos que t1 y t2 fueron derivados respectivamente. Luego tratamos de construír una extensión de phi que satisfaga el requerimiento adicional de que t1 = t2 -> t’, donde t’ es una variable fresca.

tcap :: Context -> NameSupply -> Exp -> Exp -> E(Subst,Texp)

tcap gamma ns e1 e2 = do (phi,[t1,t2]) <- tcl gamma (deplete ns) [e1,e2]) let x = next_name ns psi <- unify phi (t1, arrow t2 (Tvar x)) return (psi,psi x)

Page 22: Derivando un algoritmo de inferencia de tipos

22

Chequeando abstracciones

Cuando chequeamos una abstracción (Lambda x e) asociamos x con un esquema de la forma (Scheme [] (Tvar tvn)), donde tvn es una variable fresca. Como este esquema no tiene variables genéricas las distintas ocurrencias de x en e serán asignadas el mismo valor de la variable de tipo.

tclambda :: Context -> NameSupply -> Vname -> Vexp -> E (Subst,Texp)

tclambda gamma ns x e = do (phi,t) <- tc gamma' ns' e return (phi,arrow (phi tvn) t)) where gamma' = new_bvar(x,tvn) : gamma ns' = deplete ns tvn = next_name ns new_bvar (a,b) = (a,Scheme [] (Tvar b))

Page 23: Derivando un algoritmo de inferencia de tipos

23

Chequeando expresiones letCuando chequeamos una expresión (Let [(x,e)] e’), primero chequeamos las partes derechas de las definiciones. Luego tenemos que actualizar el contexto para asociar los esquemas apropiados con los nombres de las definiciones.

tclet :: TypeEnv -> NameSupply -> [Vname] -> [Exp] -> Eexp -> E (Subst,Texp)tclet gamma ns xs es e = do (phi,ts) <- tcl gamma ns1 es (psi, t) <- tc (add_decls (sub_ctx phi gamma) (fst (split ns0)) xs ts) (snd (split ns0)) e) return (subst_comp phi psi, t) where (ns0,ns1) = split ns

Page 24: Derivando un algoritmo de inferencia de tipos

24

Chequeando expresiones letrec

Para chequear expresiones de la forma (Letrec [(x,e)] e’) seguiremos los siguientes pasos:i) Asociaremos nuevos esquemas de tipos a los nombres de las definiciones, estos esquemas no incluirán variables genéricas.ii) Chequearemos las partes derechas de las definiciones. Si tenemos éxito, este paso generará una sustitución y una lista de tipos, los que pueden ser derivados para las expresiones si el contexto es restringido por la sustitución.iii) Unificaremos los tipos derivados para las partes derechas con los esquemas asociados a las variables, de acuerdo a las restricciones impuestas por la sustitución. Con esto respetamos el criterio de mismo tipo en parte derecha para todas las ocurrencias de los nombres.iv) Procedemos como lo hicimos con expresiones let.

Page 25: Derivando un algoritmo de inferencia de tipos

25

Chequeando patterns y case

Al chequear una expresión (case e of p1 -> e1 ... pn -> en) tenemos que asegurar lo siguiente:

i) los patrones son linealesii) la expresión e tiene un tipo Tiii) todos los patrones tienen el mismo tipo Tiv) todas las expresiones ei tienen un mismo tipo T´v) el tipo de cada expresión ei es derivado en un contexto que considere el tipo del patron pi (componentes)

|- (p1 : T) => .|- (pn : T) => n

|- e : T |- e1 : T ... n|- en : T (case)|- case e of p1 -> e1 ... pn -> en : T´

donde

Page 26: Derivando un algoritmo de inferencia de tipos

26

Chequeando patterns y case (2)|- (x : T) => x:T]

|- (C : T) ------------------------- |- (c : T) => ]

|- (p1 : T1) => . |- (pn : Tn) => n

---------------------------------------------------- |- ((p1,...,pn) : T1,...,Tn) => n

|- (p : T1) => . |- C : T1 -> T --------------------------------------------- |- ( C p : T) =>