Presentation is loading. Please wait.

Presentation is loading. Please wait.

What is a recursive module? Crary, Harper, Puri Module Systems, Fall 2002 Aleksey Kliger.

Similar presentations


Presentation on theme: "What is a recursive module? Crary, Harper, Puri Module Systems, Fall 2002 Aleksey Kliger."— Presentation transcript:

1 What is a recursive module? Crary, Harper, Puri Module Systems, Fall 2002 Aleksey Kliger

2 CHP Understand the type theory of recursive modules via a phase-splitting interpretation into a constructor and a term expression Introduce recursively-dependent signatures to accurately reflect sharing of type information in recursive modules

3 Example Recursive modules are useful for splitting a program into several independent pieces Consider the abstract syntax of a fictional ML compiler –Separate types dec and exp for the declarations and expressions –Mutually recursive datatypes

4 Example cont'd datatype exp = … | LET of dec * exp | … and dec = … | VAL of identifier * exp | … … fun make_let_val (id, e1, body) = let val d = VAL (id, e1) in LET(d,body) end … Suppose we now wish to separate the expressions and the declarations into separate modules

5 Example cont'd structure Expr = struct datatype exp = … | LET of Decl.dec * exp | … fun make_let_val (id, e1, body) = let val d = Decl.VAL(id, e1) in LET(d, body) end … end structure Decl = struct datatype dec = … | VAL of identifier * Expr.exp | … … End Fails to typecheck: neither structure can be written after the other one

6 Example cont'd What we would like is to write something like structure rec Expr = struct … End and Decl = struct … end

7 Fixpoint modules By analogy to fixpoint at term level: fix(x: .e), introduce a module-level fixpoint: fix(s:S.M) The structure variable s stands for the module being defined As with fixpoints at term level, need to ensure that the fixpoint exists and is unique. (Will return to this)

8 Opaque Recursive Modules To typecheck recursive module fix(s:S.M) suppose module variable s has signature S, and check that module M does. Opaque in the sense that when checking M the only thing we know about s is that it has signature S.

9 Opaque Recursive Modules Limitations Problem: knowing only that s has signature S is often not enough: The preceeding definition for List fails to typecheck because we do not know within the body of List that t and List.t are the same type, so cannot typecheck cons signature LIST = sig type t val nil : t val cons : int * t -> t end structure rec List :> LIST = struct datatype t = NIL | CONS of int * List.t val nil = NIL fun cons(n:int, l:t): t = CONS(n,l) end

10 Opaque Recursive Modules Limitations CHP shows a way to program around this deficiency that sacrifices efficiency: fun cons(n:int, l:t): t = case l of NIL => CONS(n,List.NIL) | CONS(n', l') => CONS(n, List.cons(n', l')) In general such a workaround not possible, instead must give List a more precise signature while typechecking the struct

11 Recursively-dependent signatures CHP solution is to introduce a signature for List which captures the dependency of t on List.t: The signature given to List depends on a structure. Incidentally, that structure is List itself structure rec List :> sig datatype t = NIL | CONS of int * List.t val nil : t val cons : int * t -> t end = struct (* as before *) end

12 Recursively-dependent signatures A module M may be given the signature  s.S if M may be given signature S[M/s] If module M can be given the rds  s.S then M also has the signature S[M/s] Back to the List example…

13 Recursively-dependent signatures We assume List has the rds, and check the struct has the same rds (this is our rule for checking fixpoints) cons is now ok because the type of l (that is, t ) is structurally equivalent to List.t s tructure rec List :> sig datatype t = NIL | CONS of int*List.t val nil : t val cons : int * t -> t end = struct datatype t = NIL | CONS of int*List.t val nil : t val cons (x:int, l:t):t = CONS(x,l) end

14 Transparency Note that the preceding example typechecked because the datatype t was defined transparently in the rds and we appealed to structural equality CHP formalize this as the formation rule for rds's. An rds  s.S is well-formed iff the type components of S are transparent and S is a well-formed sig, in the context where s:S Appealing to structural equality means we're using "equi-recursive" interpretation of recursive constructors

15 CHP Core Calculus Like HMM with singleton kinds and fixpoints at the constructor and term level kinds  ::= T | 1 | S(c) |  :  1.  2 |  :  1.  2 constructors c ::=  | ¤ |  : .c | c 1 c 2 | hc 1, c 2 i |  I (c) | 1 | c 1 ! c 2 | c 1 £ c 2 |  : .c types  ::= c |  1 ! Tot  2 |  1 !  2 |  1 £  2 | 8  : .  terms e ::= x | ¤ | x: .e | e 1 e 2 | h e 1,e 2 i |  I (e) |  : .e | e[c] | fix(x: .e) contexts  ::=  |  [  :  ] |  [x:  ] |  [  "  ] |  [x"  ]

16 Fixpoints Contractiveness condition on formation of recursive constructors to ensure that fixpoints exist and are unique Constructor  : .c is well-formed if it actually "goes somewhere", ie it unfolds to an infinite tree. Formalized with judgment c is contractive with kind , provided that  has kind  and is not contractive

17 Fixpoints Uniqueness of constructor fixpoint reflected by the bisimilarity rule:

18 Fixpoints There is a value restriction on fixpoints at the term level (more than restricting fix to lambdas because of phase-splitting considerations) Formalized by judgment which says that e is valuable under the assumption that x is not. Lambda abstractions x: .e are always valuable, moreover if e is always valuable, the lambda is deemed total, and its application is always valuable, if its argument is

19 CHP Structure Calculus Like HMM structure calculus plus new fixpoint modules and rds's constructors c ::= … | Fst s terms e ::= … | Snd s signatures S ::= [  : ,  ] |  s.S modules M ::= [c,e] | fix(s:S.M) contexts  ::= … |  [s:S] |  [s"S]

20 Fixpoint formation Fixpoint formation formalized to the judgment

21 Rds intro and elimination A module M may be given the rds  s.S if –  s.S is well-formed (next slide), –and M : S[M/s]  s.S is a dependent signature, and it depends on M Elimination: if M has the rds  s.S, M also has S[M/s]

22 Rds formation –Constructor part must be transparent –Any module M that may be given this rds may also be given an opaque signature S with all the recursive references in the static part hidden, and all the recursive references at the term level redirected to the static part –The transparent static part must be contractive where S is [  : ,  [  /Fst s]]

23 Phase splitting interpretation Like in HMM, we understand fixpoint modules via a simple structure obtained by splitting the fixpoint into a static and a dynamic component To phase-split fix(s:[  : .  ].M), suppose that in the context where s has the given signature, M phase splits into [c(Fst s), e(Fst s, Snd s)], then the fixpoint splits into [  =  : .c(  ), fix(x: , e( ,x))] Take the fixpoint of c, and redirect recursive references to the static part in e to the static part of the phase split module

24 Phase splitting rds's To phase split an rds  s.S we require that S split into [  :S(c(Fst s):  ),  (Fst s)] (ie, S has transparent type components which may refer to types in s) Then the rds splits into [  :S(  : .c(  ) :  ),  (  )] Note that recursive dependency in the static part is handled using recursive types, but dependency in the dynamic part is not essentially recursive

25 Avoiding static-on-static dependency We can get the Expr/Decl example to work without rds's using only fixpoint modules if we're willing to incur a function call overhead Consider the following opaque signatures: signature DECL = sig type exp type dec val mk_val : identifier * exp -> dec end signature EXPR = sig type exp type dec val mk_let_val : identifier * exp * exp -> exp end

26 Avoiding static-on-static dependency (cont'd) Structure rec Expr :> EXPR = struct datatype exp = … | LET of Decl.dec * exp | … type dec = Decl.dec fun mk_let_val (id, e1 : exp, body : exp) : exp = let val d = Decl.mk_val(id,e1) in LET(d,body) end … End And Decl :> DECL = struct type exp = Expr.exp datatype dec = … | VAL of identifier * Expr.exp | … fun mk_val (id, e : exp) : dec = VAL(id, e) … end

27 Practical typechecking To typecheck structure rec A : ASIG(A) = struct … end we would like to –first check that  s.ASIG(s) is well-formed, –then check that the struct has ASIG(A), given that A does. The second step is different from what we said before: to typecheck the fixpoint struct, see if it has type  s.ASIG(s), given that A has  s.ASIG(s) Would like to show that the more direct typechecking strategy is equivalent to the type theoretic method

28 Practical typechecking cont'd By the intro and elim rules for rds's, A : ASIG(A) iff A :  s. ASIG(s) Would like to know that ASIG(s) and  s.ASIG(s) are eqiuvalent in the context where s has  s.ASIG(s)

29 Practical typechecking cont'd Given s : \rho s. ASIG(s) ASIG(s) = [  :S(c(Fst s):  ),  (Fst s)](by well-formedness of  s.ASIG(s)) = [  : S(c(  : .c(  )):  ),  (  : .c(  ))](by phase-splitting s's sig) = [  : S(  : .c(  ):  ),  (  : .c(  ))](by roll up and singleton kinds) = [  : S(  : .c(  ):  ),  (  )](by singleton kinds and structures) =  s.ASIG(s)(by phase splitting) (for appropriate c, , 

30 Practical typechecking cont'd Typechecking still critically depends on equality of equi-recursive constructors at higher kind Hard problem, maybe reducible to equivalence problem of DPDAs which is decidable but no practical (efficient) algorithm

31 Iso-recursive types Type equality for equirecursive types is hard Would rather use iso-recursive types Compiling datatypes typically use iso- recursive types anyway Seems like "most of the time" recursive modules have static-on-static dependencies are within datatypes

32 Iso-recursive types Turns out need to adopt Shao's equation to compile recursive modules using iso-recursive types Let   .c(  ) be the iso-recursive type. Then Shao's equation says   .c(  ) =      )))

33 Iso-recursive types If restrict the type components of an rds to only being datatypes, then after phase- splitting, the static part of an rds will be of the form .   .c( ,  ) By invoking Shao's equation and bisimilarity, can show this is equivalent to   .c( ,  ) Ie, uses of equi-recursive types may be eliminated

34 Conclusion Phase splitting interpretation of opaque fixpoint modules and transparent rds's Rds's are a novel way of formalizing the type theory of recursive modules Relies on equi-recursive types Not clear if this is a practical language to typecheck

35 Other approaches Derek R. Dreyer, Robert Harper, and Karl Crary. Toward a Practical Type Theory for Recursive Modules. There appear to be several different ways of typechecking fixpoint modules which admit more examples, by considering the use of recursive types in the phase splitting interpretation of fixpoint modules I did not really understand this TR


Download ppt "What is a recursive module? Crary, Harper, Puri Module Systems, Fall 2002 Aleksey Kliger."

Similar presentations


Ads by Google