Upload
jeffrey-eaton
View
216
Download
3
Embed Size (px)
Citation preview
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
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
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
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
Example cont'd
• What we would like is to write something like
structure rec Expr = struct
…
End
and Decl = struct
…
end
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)
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.
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 -> tendstructure 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
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
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 -> tend = struct (* as before *)end
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…
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
structure rec List :> sig datatype t = NIL | CONS of int*List.t val nil : t val cons : int * t -> tend = struct datatype t = NIL | CONS of int*List.t val nil : t val cons (x:int, l:t):t = CONS(x,l)end
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
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 | c1 c2 | hc1, c2i | I(c) | 1 | c1! c2 | c1£ c2 | :.ctypes ::= c | 1!Tot2 | 1!2 | 1£2 | 8:.terms e ::= x | ¤ | x:.e | e1 e2 | h e1,e2i | I(e) | :.e | e[c] | fix(x:.e)contexts ::= | [:] |[x:] | ["] | [x"]
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
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
CHP Structure Calculus
• Like HMM structure calculus plus new fixpoint modules and rds's
constructors c ::= … | Fst sterms e ::= … | Snd ssignatures S ::= [:, ] | s.Smodules M ::= [c,e] | fix(s:S.M)contexts ::= … | [s:S] | [s"S]
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]
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]]
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
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
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 -> decendsignature EXPR = sig type exp type dec val mk_let_val : identifier * exp * exp -> expend
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 …EndAnd 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
Practical typechecking
• To typecheck structure rec A : ASIG(A) = struct … endwe 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
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)
Practical typechecking cont'dGiven 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,,
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
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
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() = )))
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
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
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