Upload
margery-nelson
View
225
Download
5
Tags:
Embed Size (px)
Citation preview
Singleton Kinds and Singleton Types
Christopher A. StoneAugust 2, 1999
Thesis CommitteeBob Harper, chair
Peter LeeJohn Reynolds
Jon Riecke (Bell Laboratories)
Dissertation Summary
• Study of language with singleton kinds and singleton types– Models intermediate representation of TILT
compiler
• Proofs of important language properties– Decidability of typechecking– Algorithms for typechecking
• Context-sensitive type-equivalence algorithm
– Soundness of type system
• Here I will concentrate on singleton kinds.
TILT Compiler for Standard ML
• Reimplementation of the TIL prototype [Tarditi et. al. 96]
– Compiles full Standard ML– Handles separate compilation, larger
programs
• Key technologies– Intensional Polymorphism [Harper & Morrisett 95]
– Typed Compilation [Morrisett 95]
Typed Compilation
• Uses strongly-typed intermediate language(s)
• Compiler passes preserve well-typedness
prog1 : t
prog2 : t
Common Subexpression Elimination
prog1 : t1
prog2 : t2
Closure Conversion
Advantages of Typed Compilation
• Retains information– For optimizations– For improved code generation– For safety certificates
• Also a handy compiler debugging tool!– Typechecking after each transformation
finds many common implementation errors.– Improves compiler robustness
One Pass: Phase Splitting
• SML module system– Structures package types, values, and structures– Functors map modules to modules
• Phase Splitting transformation [Harper et al. 90]
– Purpose: Translate from language with modules to a language without
– Every module splits into a type part and a value part:
– Interfaces split in a parallel fashion
An Interface for Queuesstructure Queue: sig type elem type queue val empty : queue val enqueue : elem * queue -> queue val dequeue : queue -> elem * queueend
Phase Splitting Queuesstructure Queue: sig type elem type queue val empty : queue val enqueue : elem * queue -> queue val dequeue : queue -> elem * queueend
Queue_c : {elem : TYPE, queue : TYPE}
Queue_r : {empty : …, enqueue : …, dequeue : …}
Queues of Stringsstructure StringQueue: sig type elem = string type queue val empty : queue val enqueue : elem * queue -> queue val dequeue : queue -> elem * queueend
Queues of Stringsstructure StringQueue: sig type elem = string type queue val empty : queue val enqueue : elem * queue -> queue val dequeue : queue -> elem * queueend
Makes elem a synonym for string
Queues of Stringsstructure StringQueue: sig type elem = string type queue val empty : queue val enqueue : elem * queue -> queue val dequeue : queue -> elem * queueend
StringQueue_c : {elem : ????, queue : TYPE}
StringQueue_r : {empty : …, enqueue : …, dequeue : …}
A First Trystructure StringQueue: sig type elem = string type queue val empty : queue val enqueue : elem * queue -> queue val dequeue : queue -> elem * queueend
StringQueue_c : {elem : TYPE, queue : TYPE}
StringQueue_r : {empty : …, enqueue : …, dequeue : …}
How to remember elem string?
• Option 1: Substitution– Replace elem with string everywhere [Shao 99]
• Must remove indirect references as well
– But in general this causes duplication of types• Instead of string, might have a big, complex type• Replacing elem with this type everywhere could cause
blow-up in code size• In TILT types can correspond to run-time
computations
How to remember elem string?
• Option 1: Substitution– Replace elem with string everywhere [Shao 99]
• Must remove indirect references as well
– But in general this causes duplication of types• Instead of string, might have a big, complex type• Replacing elem with this type everywhere could cause
blow-up in code size• in TILT types can correspond to run-time computations
• Option 2: Put definitions in the kind structure
What is a kind?
• In SML, types form a little language
• Kinds are the “types of types”
– key and key pair are well-formed types– pair and pair pair are not
type key = inttype ‘a pair = ‘a * ‘a
key :: TYPEpair :: TYPE TYPE
Singleton Kinds
• S(A) is “kind of all types interchangeable with A”– i.e., B :: S(A) if and only if B A
• Advantages– Single construct isolating issues of type
definitions – Not conflated with ML module system – Syntactically simple– Necessary for type-preserving polymorphic
closure conversion [Minamide et al. 96]
Queues of Strings with Singletons
structure StringQueue: sig type elem = string type queue val empty : queue val enqueue : elem * queue -> queue val dequeue : queue -> elem * queueend
StringQueue_c : {elem :: S(string), queue :: TYPE}
StringQueue_r : {empty : …, enqueue : …, dequeue : …}
Typechecking with Singletons
• Want to be able to typecheck after phase-splitting– Initial attempts to program typechecker
failed• Rejected valid inputs• Went into infinite loops
– Question: is typechecking even decidable?
• The key to typechecking is determining equivalence of type constructors
MIL0 Syntax (excerpt)
• Type Constructors A,B ::= int | bool | ... | |:K.A | A B | <A,B> | 1A | 2A
• Kinds K,L ::= T | S(A) | :K.L (K L) | :K.L (K L)
Static Semantics
• Standard rules for dependent kinds and -equivalence of constructors, plus:– Singleton introduction and elimination
• If A : T then A : S(A)
• If A : S(B) then A B : T
– Subkinding relation: S(A) T• Lifted to and kinds as usual
– Two non-standard typing rules• Ensures types preserved under -equivalence• e.g., can show if f : T T then f : :T. S(f)
Singletons make Equivalence Interesting
• Dependency on kinds of free variables: :: TS(int) 2 int :: T
:: ::T.S() 1 2 :: T
:: TS(int) :T.int :: TT
Singletons make Equivalence Interesting
• Dependency on kinds of free variables: :: TS(int) 2 int :: T
:: ::T.S() 1 2 :: T
:: TS(int) :T.int :: TT
• Dependency on classifying kind– Cannot show ::T.int ::T. :: TT
Singletons make Equivalence Interesting
• Dependency on kinds of free variables: :: TS(int) 2 int :: T
:: ::T.S() 1 2 :: T
:: TS(int) :T.int :: TT
• Dependency on classifying kind– Cannot show ::T.int ::T. :: TT
– But, ::T.int :T. :: S(int)T holds!
Singletons make Equivalence Interesting
• Dependency on kinds of free variables: :: TS(int) 2 int :: T
:: ::T.S() 1 2 :: T
:: TS(int) :T.int :: TT
• Dependency on classifying kind– Cannot show ::T.int ::T. :: TT
– But, ::T.int :T. :: S(int)T holds!
• Interestingly, -equivalence is admissible!
Typechecking with Singletons
• Not immediately obvious that typechecking is decidable in the presence of singletons
• Standard context-free rewriting techniques not directly applicable– Adapting these techniques can be hard
• e.g., Lillibridge, Curien and Ghelli
• My method (inspired by Coquand)– Define a direct comparison algorithm– Prove it correct using logical relations
Algorithmic Equivalence
• Deterministic rules for A B :: K– Expect agreement with A B :: K
• for well-formed A, B
• Strategy: reduce to tests at kind T
Algorithmic Equivalence
• Deterministic rules for A B :: K– Expect agreement with A B :: K
• for well-formed A, B
• Strategy: reduce to tests at kind T A B :: ::K.L if 1A 1B :: K
and 2A 2B :: [1A/]L
Algorithmic Equivalence
• Deterministic rules for A B :: K– Expect agreement with A B :: K
• for well-formed A, B
• Strategy: reduce to tests at kind T A B :: ::K.L if 1A 1B :: K
and 2A 2B :: [1A/]L A B :: ::K.L if ::K A B :: L
Algorithmic Equivalence
• Deterministic rules for A B :: K– Expect agreement with A B :: K
• for well-formed A, B
• Strategy: reduce to tests at kind T A B :: :K.L if 1A 1B :: K
and 2A 2B :: [1A/]L A B :: :K.L if ::K A B :: L
A B :: S(C) always
• At kind T head-normalize, compare subcomponents– Includes expansion of definitions
Correctness Proof
• Hardest part is showing completeness of the algorithm w.r.t. provable equivalence
• Approach: logical relations– Define “logical” equivalence– Show logical equivalence implies
algorithmic equivalence ()– Show provable equivalence () implies
logical equivalence.
Logical Relations
• Provable equivalence depends on context and classifying kind
• Also, relating open terms• Suggests Kripke logical relations (relations
indexed by a “world” and a kind)– In our case, a world is a typing context– Ordering on worlds is prefix ordering
A Natural Attempt
• Relatively standard Kripke logical relations:– A is B in T [] iff A B :: T
– A is B in ::K.L [] iff ’ if A’ is B’ in K [’] then A A’ is B B’ in [A’/]L [’]
• This proof nearly goes through
The Problem
• We must show logical equivalence is symmetric and transitive
• This requires showing the algorithm symmetric and transitive
• Arbitrary choices in presentation of the algorithm prevent a direct proof, e.g.,
A B :: ::K.L if 1A 1B :: K and 2A 2B :: [1A/]L
A Revised Algorithm
• Idea: maintain a context and a classifier for each constructor– 6 place relation 1 A1 :: K1 2 A2 :: K2
– Uses both alternatives at choice points• one on each side
– Maintains invariant that 1 2 and K1 K2
• Correctness of revised algorithm implies correctness of original algorithm
Revised Logical Relations
• Because algorithm has two classifiers and two contexts, logical relations must as well:– A1 in K1 [1] is A2 in K2 [2]
• Definition retains same flavor as before– e.g., two constructors are related at (related)
-kinds at two worlds if for each pair of future worlds …
• Does not seem possible to formulate as a standard Kripke relation indexed by pairs (1,2) and (A1,A2)
Summary of Decidability
• Soundness provable directly: if A B :: K then A B :: K
• Proof just outlined shows: if A B :: K then A B :: K
• Easy corollary: Algorithm terminates on all inputs
• Thus equivalence is decidable.• Thus validity of type constructors and their
kinds is decidable [Stone and Harper 00]
Singleton Types
• S(v : ) is the type of terms of type equivalent to v– Restriction: only values may appear in singletons– Strong equivalence (no )
• Equivalence only depends on typing context, not classifier
• Similar proof strategy shows typechecking decidable
• Potential use: TILT cross-module inlining– Interfaces can expose code of module
components
Type Soundness
• Want to show “well-typed programs don’t go wrong”
• Mostly standard proof outline– Evaluation preserves well-typedness– Well-typed programs don’t get stuck”
• But, proof requires consistency property– e.g., int and bool are not provably equivalent– Follows directly by correctness of equivalence
algorithm
Adding Intensional Polymorphism
• Constructs for runtime case analysis (and primitive recursion) of type constructors – Permits improved calling conventions, data
representation even when types statically unknown [Harper and Morrisett 95]
• All the proofs go through with relatively minor modifications.– Relatively robust proof technique
Summary of Contributions
• Thorough study of a language MIL0 with singleton kinds and singleton types– Two very different forms of equivalence
• Results for type soundness and decidability• Typechecking algorithms
– TILT compiler uses those for singleton kinds– General framework for context-sensitive
equivalences
Open Questions
• Improved theory of singleton types– Currently appears to require too many type
annotations to be practical– S(v) instead of S(v : t) ?
• Nontrivial equivalences between recursive types– Extension in TILT implementation works in
practice– No obvious way to make algorithm
obviously transitive.
Related Work
• Aspinall [94, 97]– Studied -calculus with singleton types– Somewhere between my singleton types and
kinds• Has -equivalence, not , strong equivalence for
lambda abstractions, singletons contain type annotations
– Gave a PER model– Showed existence of principal types– No typechecking or equivalence algorithm
(though conjectured decidable)