710 likes | 864 Vues
Singleton Kinds. Derek Dreyer Fall Semester, 2002. Part 1: Motivation. Type-Directed Compilation. Retain types throughout compilation using typed intermediate languages . Enables type-based optimizations Typechecking after compiler phases improves robustness of compiler
E N D
Singleton Kinds Derek Dreyer Fall Semester, 2002
Type-Directed Compilation • Retain types throughout compilation using typed intermediate languages. • Enables type-based optimizations • Typechecking after compiler phases improves robustness of compiler • Makes it possible to target typed assembly language
The TIL Compiler • Type-directed compiler for Core ML (Tarditi et al., PLDI 96) • Highly optimized • Intensional type analysis (Harper and Morrisett, POPL 95) • Other traditional optimizations, not previously implemented for functional languages (Tarditi’s thesis, 97)
Problems with TIL • Large type representations • Whole-program optimizations • Doesn’t support modules!
TILT (TIL Two) • Complete re-engineering of TIL • Compiles full Standard ML • Supports separate compilation • Sharing of type representations via singleton kinds.
Structure of TILT Standard ML Elaboration High-level Intermediate Language HIL Phase-Splitting Mid-level Intermediate Language MIL Optimizations Code Generation
HIL • Described in Harper-Stone 98, along with the elaboration algorithm • Based on Harper-Lillibridge (HL) formalism, restricted to second-class modules • Coercive signature subtyping (e.g. dropping components) handled by elaboration • HIL subtyping (like HL’s) strictly about forgetting of type identity
Phase-Splitting • Erase sealing • Sealing just restricts the use of a module • Unnecessary after elaboration/typechecking • Split modules and signatures into two parts: • The static part has the type components • The dynamic part has the term components • Once separated, both parts can be written in the MIL.
Phase-Splitting • X : sig type t val x : t end • X_Static : sig type t end • X_Dynamic : sig val x : X_Static.t end
MIL Syntactic Classes • X_Static is a type constructor, which is classified by a kind. • X_Dynamic is a term, which is classified by a type.
Simple Kinds • Base kind is T, the kind of types. • int : T, bool : T • Function and product kinds: • K1! K2, K1£ K2 • Example: • type (a,b) pair = a * b • pair = l(g:T £ T). p1g * p2g • pair : (T £ T) ! T
Type Definitions • X : sig type t type u = A (* where A is big *) val x : t end • X_Static : T (* just the “t” component *) • X_Dynamic : t [A / u, X.Static / t]
Dependent & Singleton Kinds(Stone and Harper, POPL 00) • To avoid space blowup, introduce • new singleton kind S(A) • B : S(A) B ´ A : T • S(A) 6 T • Must also generalize ! and £ to: • Dependent function kind : Pa:K1.K2 • Dependent sum kind : Sa:K1.K2
Dependent S Example • X_Static : S t:T.S(A) • X_Dynamic : t [p1(X_Static) / t, p2(X_Static) / u] • We haven’t lost any equivalences: p2(X_Static) ´ A[p1(X_Static) / t]
Dependent P Example • F : funsig (X:sig type elem end) -> sig type elem = X.elem type set ... end • F_Static : Pa : T. (S(a) £ T)
Advantages of Singletons • Carry advantages of translucency over to the MIL level: • Precise control of type propagation • Sharing of type representations • Clean handling of open-scope type definitions.
Syntax • A ::= a | la:K.A | A1 A2 | <a=A1,A2> | p1A | p2A • K ::= T | Pa:K1.K2 | Sa:K1.K2 | S(A) • G ::= ² | G, a:K
Typing Rules • Standard dependent typing rules:
Selfification • Recall the idea of selfification from the translucent sums formalism: • If X : sig type t end, then X : sig type t = X.t end.
Selfification • Recall the idea of selfification from the translucent sums formalism: • If X : sig type t end, then X : sig type t = X.t end. • Selfification arises naturally here as singleton introduction:
Everything is Determinate • Unlike Harper-Lillibridge, there is no “value restriction” here on what can be selfified. • Every type constructor A is determinate because the language is pure and there is no sealing.
Higher-Order Selfification • If X : sig structure A : SIGA structure B : SIGB end,then X : sig structure A : Selfify(SIGA, X.A) structure B : Selfify(SIGB, X.B) end
Higher-Order Selfification • If X : sig structure A : SIGA structure B : SIGB end,then X : sig structure A : Selfify(SIGA, X.A) structure B : Selfify(SIGB, X.B) end • Achieved in type theory by a “kind strengthening” rule:
Higher-Order Selfification • If F : funsig (X:SIGA) ! SIGB,then F : funsig (X:SIGA) ! Selfify(SIGB, F(X)) • Wait a sec! Can we do that?What about the “generativity” of F? • Well...the types in the body of F can’t really depend on run-time conditions.
Higher-Order Selfification • Type constructors clearly can be selfified: • list : T ! T • Principal kind of list is Pa:T.S(list a)
Higher-Order Selfification • Type constructors clearly can be selfified: • list : T ! T • Principal kind of list is Pa:T.S(list a) • And now for the kind strengthening rule: • Left premise necessary to ensure a not in the free variables of A
Subkinding • Subkinding very similar to signature subtyping in Harper-Lillibridge:
Question • So now that we’ve defined the typing judgment, onto equivalence. • But wait: if A ´ B : T, then how do we prove A : S(B)?
Answer • So now that we’ve defined the typing judgment, onto equivalence. • But wait: if A ´ B : T, then how do we prove A : S(B)? • Answer: • A : S(A), and if A ´ B, then S(A) 6 S(B), so by subsumption A : S(B).
Type Constructor Equivalence • Standard structural rules • Reflexive, symmetric, transitive • Beta-equivalence rules • Subsumption rule • Singleton elimination rule:
Extensionality • Eta-equivalence provided in the form of extensionality rules:
Admissibility of Eta • If F : Pa:K1.K2, then assuming a:K1,we have (la:K1.F(a))(a) ´ F(a) : K2,so by extensionality, (la:K1.F(a)) ´ F. • Cool property of extensionality: • Self rules are just reflexive forms of equivalence rules (nice for proofs later on • Can think of self rules as follows:
Higher-Order Singletons • S(A) only valid if A : T. • In fact, S(A : K) at higher K is definable so that the singleton rules hold at any kind: • If A : S(B : K) and B : K, then A ´ B : S(B : K). • If A : K, then A : S(A : K) and S(A : K) 6 K. • If A1´ A2 : K1 and K16 K2,then S(A1 : K1) 6 S(A2 : K2).
Higher-Order Singletons • Extensionality important here! • S(A : K) classifies constructors that are extensionally equal to the eta-expansion of A.
Admissibility of Beta • It turns out that beta-equivalence is admissible in the presence of higher-order singletons:
Subtleties of Equivalence • Constructor equivalence obviously depends on the context: • a:T, b:T `a´b : T. • a:T, b:S(a) `a´b : T.
Subtleties of Equivalence • It less obviously depends on the kind at which the constructors are compared. • `la:T.a´la:T.int : T ! T. • `la:T.a´la:T.int : S(int) ! T. • This turns out to have a profound effect on the type equivalence algorithm.
Typechecking Algorithm • Want to determine if G` A : K • First synthesize principal kind KP of A • Then check if G` KP6 K
Principal Kind Synthesis • G` A * K synthesizes principal K of A • Synthesis for intro and elim forms corresponds precisely to typing rules • For variables, we selfify: • G`a* S(a : G(a)) • Theorem: If G` A : K, then G` A * KP and G` KP6 K.
Question • How do we prove theorem for self rule?
Answer • How do we prove theorem for self rule? • Answer: Need to generalize theorem: • If G` A : K, then G` A * KP and G` KP6 S(A : K). (Lemma 2.2 in Stone-Harper)
Subkinding • Subkinding is syntax-directed • To decide S(A) 6 S(B), we need to decide whether A ´ B : T • Decidability of typechecking reduces to decidability of constructor equivalence
Equivalence Algorithm • Typically, to decide whether A ´ B, we “normalize-and-compare” • Define some reduction relation that is confluent and strongly normalizing • Then A ´ B iff their normal forms are equal • Doesn’t work when equivalence is kind- and context-dependent • e.g. la:T.a´la:T.int : S(int) ! T
Equivalence Algorithm • Inspired by [Coquand 91] to define equivalence algorithm directly • Algorithmic judgment: G` A1, A2 : K • Big picture: • Constructor equivalence reduces to type equivalence (i.e. G` A1, A2 : T) • At kind T, reduce A1 and A2 to paths p1 and p2, expanding out singletons along the way • Compare p1 and p2 structurally
Reducing to Type Equivalence • When checking G` A1, A2 : K, we can assume that G` A1 : K and G` A2 : K • If K is a singleton S(A), we can immediately accept because A1´ A and A2´ A, so:
Reducing to Type Equivalence • At higher kind, algorithmic equivalence is precisely extensional equivalence: • Note that the kinds are “smaller” in the premises than in the conclusions.
Reducing to Type Equivalence • At base kind T, we reduce A1 and A2 to a form in which we can structurally compare them, namely path form, also called weak head normal form: • Note: we could have done the singleton case this way as well.