Title: Phantom Types and Subtyping
1Phantom Types and Subtyping
- Matthew Fluet Riccardo Pucella
- Dept. of Computer Science
- Cornell University
2The Setting (I)
- Modern strongly typed functional language
- Parametric polymorphism
- val id ? ? ? fn x gt x
- Type constraints
- val idInt int ? int fn x gt x
- Datatypes and type constructors
- datatype ? tree
- Leaf of ?
- Node of ? tree ? ? tree
3The Setting (II)
- Modern strongly typed functional language
- Limited expressibility at foreign function
interfaces - No polymorphic types
- No user defined datatypes
- No primitive notion of subtyping
4The Problem
- datatype atom I of Int B of bool
- fun mkI (iint)atom I(i)
- fun mkB (bbool)atom B(b)
- fun toString (vatom)string ...
- fun double (vatom)atom ...
- fun conj (v1atom, v2atom)atom ...
toString (mkI 1) ? 1 toString (mkB
false) ? false
double (mkB true) ? run-time
error conj (mkI 3, mkB true) ? run-time error
5Wish List
- Raise compile-time type errors on domain
violations rather than run-time errors - toString should apply to all atoms
- double should only apply to integer atoms
- conj should only apply to boolean atoms
- Preserve the implementation
- Would like to treat integer and boolean atoms as
subtypes of all atoms
6A First Solution (I)
- type All, Int, Bool
- datatype ? atom I of int B of bool
- fun mkI (iint)Int atom ...
- fun mkB (bbool)Bool atom ...
- fun toString (vAll atom)string ...
- fun double (vInt atom)Int atom ...
- fun conj (v1Bool atom, v2Bool atom)Bool atom
...
double (mkB true) ? compile-time type error
Int atom ? Bool atom conj (mkI 3, mkB true) ?
compile-time type error Bool atom ? Int atom
toString (mkI 1) ? compile-time type
error Int atom ? All atom toString (mkB false)
? compile-time type error Bool atom ? All atom
7Phantom Types
- type All, Int, Bool
- datatype ? atom I of int B of bool
- fun mkI (iint)Int atom ...
- fun mkB (bbool)Bool atom ...
- Phantom types
- Abstract types that need not have any
corresponding run-time values - Phantom type variables
- Type instantiations of ? in ? atom do not
contribute to the run-time representation of atoms
8A First Solution (II)
- type All, Int, Bool
- datatype ? atom I of int B of bool
- fun mkI (iint)Int atom ...
- fun mkB (bbool)Bool atom ...
- fun toString (vAll atom)string ...
- fun double (vInt atom)Int atom ...
- fun conj (v1Bool atom, v2Bool atom)Bool atom
... - fun intToAll (vInt atom)All atom v
- fun boolToAll (vBool atom)All atom v
toString (intToAll (mkI 1)) ? 1
toString (boolToAll (mkB false)) ? false
9A Better Solution
- type All, Int, Bool
- datatype ? atom I of int B of bool
- fun mkI (iint)Int atom ...
- fun mkB (bbool)Bool atom ...
- fun toString (v? atom)string ...
- fun double (vInt atom)Int atom ...
- fun conj (v1Bool atom, v2Bool atom)Bool atom
...
double (mkB true) ? compile-time type error
Int atom ? Bool atom conj (mkI 3, mkB true) ?
compile-time type error Bool atom ? Int atom
toString (mkI 1) ? well typed Int atom
unifies with ? atom toString (mkB false) ? well
typed Bool atom unifies with ? atom
10The Phantom Types Technique
- Use a superfluous type variable and type
constraints to encode extra information - Underlies many interesting uses of type systems
- Foreign function interfaces
- Embedded languages
- Uncaught exception analysis
- A folklore technique
11Contributions
- A general encoding of subtyping hierarchies into
phantom types - A formalization of one use of the phantom types
technique
12Outline
- A recipe for interfaces and implementations
- Encoding subtyping hierarchies
- Bounded polymorphism
- Formalization
13From Subtyping to Polymorphism
- Features of the example
- An underlying primitive type of values
- A set of operations
- A hierarchy of implicit subtypes
- mkI 1
- Int
- int atom
- toString
- All ? string
- ? atom ? string
14The Recipe
- Given
- A primitive type ?p
- An implicit subtyping hierarchy ?1,,?n
- An implementation of ?p and its operations
- Derive
- A safe interface (the types)
- A safe implementation (the code)
- Restrictions
- Shared representation and operations
15Applying the Recipe
- Given
- A primitive type atom
- An implicit subtyping hierarchy All, Int, Bool
- An implementation structure Atom
- Derive
- A safe interface signature SAFE_ATOM
- A safe implementation structure SafeAtom
16Deriving the Interface (I)
- ??1?C unifies with ??2?A iff ?1 ? ?2
- Introduce type ? ?
- Encode each implicit type ? as ??? ?
- ??1? unifies with ??2? iff ?1 ? ?2
- Example
- ?All?C unit ?All?A ?
- ?Int?C int ?Int?A int
- ?Bool?C bool ?Bool?A bool
17Deriving the Interface (II)
- Use concrete encodings in all covariant type
positions - Use abstract encodings in most contravariant type
positions
18Deriving the Interface (III)
- signature ATOM sig
- type atom
- val mkI int -gt atom
- val mkB bool -gt atom
- val toString atom -gt string
- val double atom -gt atom
- val conj atom atom -gt atom
- end
- ?
- signature SAFE_ATOM sig
- type ? atom
- val mkI int -gt ?Int?C atom
- val mkB bool -gt ?Bool?C atom
- val toString ?All?A atom -gt string
- val double ?Int?A atom -gt ?Int?C atom
- val conj ?Bool?A atom ?Bool?A atom -gt
?Bool?C atom - end
19Applying the Recipe
- Given
- An abstract type atom ?p
- An implicit subtyping hierarchy All, Int, Bool
?p - An implementation structure Atom ?p
- Derive
- A safe interface signature SAFE_ATOM
- A safe implementation structure SafeAtom
?
20Deriving the Implementation (I)
- Need a type ? ? isomorphic to ?p
- the type system should consider ?1 ? and ?2 ?
equivalent iff ?1 and ?2 are equivalent - Opaque signature constraint
- Hides all type implementation details
21Deriving the Implementation (II)
- structure SafeAtom1gt SAFE_ATOM struct
- type ? atom Atom.atom
- val mkI Atom.mkI
- val mkB Atom.mkB
- val toString Atom.toString
- val double Atom.double
- val conj Atom.conj
- end
22Applying the Recipe
- Given
- An abstract type atom ?p
- An implicit subtyping hierarchy All, Int, Bool
?p - An implementation structure Atom ?p
- Derive
- A safe interface signature SAFE_ATOM
- A safe implementation structure SafeAtom
?
?
23Encoding Subtyping Hierarchies (I)
- Powerset lattice encoding
- S s1,,sn is a finite set
- Ordered by inclusion
- X ? S
- ?X?C t1 ? ? tn where ti unit if si ? X
- unit z otherwise
- ?X?A t1 ? ? tn where ti ?i if si ? X
- ?i z otherwise
24Encoding Subtyping Hierarchies (II)
- ?All?C unit ? unit
- ?Int?C unit ? unit z
- ?Bool?C unit z ? unit
- ?None?C unit z ? unit z
- ?All?A ?1 ? ?2
- ?Int?A ?1 ? ?2 z
- ?Bool?A ?1 z ? ?2
- ?None?A ?1 z ? ?2 z
25Encoding Subtyping Hierarchies (III)
- Any finite hierarchy can be embedded in the
powerset lattice of a set S - Better encodings for specific classes of
hierarchies
26Bounded Polymorphism
- Extends both parametric polymorphism and
subtyping - double ???Int.? ? ?
- toString ???All.? ? string
- plus ???Int.(? ? ?) ? ?
- Provides a connection between type instantiation
and subtyping - We can safely encode a restricted form of bounded
polymorphism using a simple extension of our
recipe
27Formalization
- Translation
- From a language with a restricted form of bounded
polymorphism - To a language with parametric polymorphism
- Using the recipe given earlier
- See paper for details
28Conclusion
- Use type equivalence to encode information in a
free type variable - Use unification to enforce a particular relation
on the information - Practical issues
- complexity of types
29(No Transcript)
30The Problem (II)
- datatype atom I of int B of bool
- fun mkI (iint)atom I(i)
- fun mkB (bbool)atom B(b)
- fun toString (vatom)string
- case v of
- I(i) gt Int.toString(i)
- B(b) gt Bool.toString(b)
- fun double (vatom)atom
- case v of
- I(i) gt I (2 i)
- _ gt raise (Fail type mismatch)
- fun conj (v1atom, v2atom)atom
- case (v1,v2) of
- (B(b1),B(b2)) gt B (b1 andalso b2)
- _ gt raise (Fail type mismatch)
31A Better Solution (II)
- type All Int Bool unit
- datatype ? atom I of int B of bool
- fun mkI (iint)Int atom I(i)
- fun mkB (bbool)Bool atom B(b)
- fun toString (v? atom)string
- case v of
- I(i) gt Int.toString(i)
- B(b) gt Bool.toString(b)
- fun double (vInt atom)Int atom
- case v of
- I(i) gt I (2 i)
- _ gt raise (Fail type mismatch)
- fun conj (v1Bool atom, v2Bool atom)Bool atom
- case (v1,v2) of
- (B(b1),B(b2)) gt B (b1 andalso b2)
- _ gt raise (Fail type mismatch)
32Bounded Polymorphism (I)
- Extends both parametric polymorphism and
subtyping - ????.???
- ????.(???)??
33Bounded Polymorphism (II)
- Example Nat ? Int
- double ???Int.? ? ?
- ?? ? ?? where ?? ?Int?A ?
- plus ???Int.(? ? ?)??
- ?? ? ?? ? ?? where ?? ?Int?A ?
- plus (mkI 1, natToInt (mkN 2))
34Bounded polymorphism (III)
- Limitations
- Type variable bounds
- ????.????.(???) ? ?
- ?? ? ?? ? ?? where ?? ???A ? and ?? ???A ?
- Functional subtyping
- ???(?1 ? ?2).? ? ?2
- ?? ? ??2?C ? where ?? ??1?C ? ? ??2?A ?
35Formalization (II)
- let f1 ??1??1 . ?x?1. c1 x in
-
- let fn ??n??n . ?x?n. cn x in
-
- ?
- safe interface types