Post on 21-Jan-2016
description
Phantom Types and Subtyping
Matthew Fluet Riccardo Pucella
Dept. of Computer ScienceCornell University
TCS2002 2
The Setting (I)
• Modern strongly typed functional language– Parametric polymorphism
• val id: = fn x => x
– Type constraints• val idInt: int int = fn x => x
– Datatypes and type constructors• datatype tree = Leaf of | Node of tree tree
TCS2002 3
The 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
TCS2002 4
The Problem
datatype atom = I of Int | B of boolfun mkI (i:int):atom = I(i)fun mkB (b:bool):atom = B(b)fun toString (v:atom):string = ...fun double (v:atom):atom = ...fun conj (v1:atom, v2:atom):atom = ...
toString (mkI 1) “1”
toString (mkB false) “false”
double (mkB true) run-time error
conj (mkI 3, mkB true) run-time error
TCS2002 5
Wish 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
TCS2002 6
A First Solution (I)
type All, Int, Booldatatype atom = I of int | B of boolfun mkI (i:int):Int atom = ...fun mkB (b:bool):Bool atom = ...fun toString (v:All atom):string = ...fun double (v:Int atom):Int atom = ...fun conj (v1:Bool atom, v2:Bool 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 atomtoString (mkI 1) compile-time type error; Int atom All atom
toString (mkB false) compile-time type error; Bool atom All atom
TCS2002 7
Phantom Types
type All, Int, Booldatatype atom = I of int | B of boolfun mkI (i:int):Int atom = ...fun mkB (b:bool):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
TCS2002 8
A First Solution (II)
type All, Int, Booldatatype atom = I of int | B of boolfun mkI (i:int):Int atom = ...fun mkB (b:bool):Bool atom = ...fun toString (v:All atom):string = ...fun double (v:Int atom):Int atom = ...fun conj (v1:Bool atom, v2:Bool atom):Bool atom = ...fun intToAll (v:Int atom):All atom = vfun boolToAll (v:Bool atom):All atom = v
toString (intToAll (mkI 1)) “1”
toString (boolToAll (mkB false)) “false”
TCS2002 9
A Better Solution
type All, Int, Booldatatype atom = I of int | B of boolfun mkI (i:int):Int atom = ...fun mkB (b:bool):Bool atom = ...fun toString (v: atom):string = ...fun double (v:Int atom):Int atom = ...fun conj (v1:Bool atom, v2:Bool 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 atomtoString (mkI 1) well typed; Int atom unifies with atom
toString (mkB false) well typed; Bool atom unifies with atom
TCS2002 10
The 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
TCS2002 11
Contributions
• A general encoding of subtyping hierarchies into phantom types
• A formalization of one use of the phantom types technique
TCS2002 12
Outline
• A recipe for interfaces and implementations
• Encoding subtyping hierarchies• Bounded polymorphism• Formalization
TCS2002 13
From 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
All
Int Bool
TCS2002 14
The 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
TCS2002 15
Applying 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
TCS2002 16
Deriving the Interface (I)
• Introduce type • Encode each implicit type as
– 1 unifies with 2 iff 1 2
– 1C unifies with 2A iff 1 2
• Example:AllC = unit AllA = IntC = int IntA = int
BoolC = bool BoolA = bool
TCS2002 17
Deriving the Interface (II)
• Use concrete encodings in all covariant type positions
• Use abstract encodings in most contravariant type positions
TCS2002 18
Deriving the Interface (III)
signature ATOM = sig type atom val mkI: int -> atom val mkB: bool -> atom val toString: atom -> string val double: atom -> atom val conj: atom * atom -> atomend
signature SAFE_ATOM = sig type atom val mkI: int -> IntC atom val mkB: bool -> BoolC atom val toString: AllA atom -> string val double: IntA atom -> IntC atom val conj: BoolA atom * BoolA atom -> BoolC atomend
TCS2002 19
Applying 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
TCS2002 20
Deriving 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
TCS2002 21
Deriving the Implementation (II)
structure SafeAtom1:> 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.conjend
TCS2002 22
Applying 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
TCS2002 23
Encoding Subtyping Hierarchies (I)
• Powerset lattice encoding– S = {s1,…,sn} is a finite set– Ordered by inclusion
X SXC = t1 … tn where ti = unit if si X
unit z otherwiseXA = t1 … tn where ti = i if si X
i z otherwise
TCS2002 24
Encoding Subtyping Hierarchies (II)
AllC = unit unit
IntC = unit unit z
BoolC = unit z unit
NoneC = unit z unit z
AllA = 1 2
IntA = 1 2 zBoolA = 1 z 2
NoneA = 1 z 2 z
All = {s1, s2}
Int = {s1} Bool = {s2}
None = {}
TCS2002 25
Encoding Subtyping Hierarchies (III)
• Any finite hierarchy can be embedded in the powerset lattice of a set S
• Better encodings for specific classes of hierarchies
TCS2002 26
Bounded 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
TCS2002 27
Formalization
• 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
TCS2002 28
Conclusion
• 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
TCS2002 29
TCS2002 30
The Problem (II)
datatype atom = I of int | B of boolfun mkI (i:int):atom = I(i)fun mkB (b:bool):atom = B(b)fun toString (v:atom):string = case v of I(i) => Int.toString(i) | B(b) => Bool.toString(b)fun double (v:atom):atom = case v of I(i) => I (2 * i) | _ => raise (Fail “type mismatch”)fun conj (v1:atom, v2:atom):atom = case (v1,v2) of (B(b1),B(b2)) => B (b1 andalso b2) | _ => raise (Fail “type mismatch”)
TCS2002 31
A Better Solution (II)
type All = Int = Bool = unitdatatype atom = I of int | B of boolfun mkI (i:int):Int atom = I(i)fun mkB (b:bool):Bool atom = B(b)fun toString (v: atom):string = case v of I(i) => Int.toString(i) | B(b) => Bool.toString(b)fun double (v:Int atom):Int atom = case v of I(i) => I (2 * i) | _ => raise (Fail “type mismatch”)fun conj (v1:Bool atom, v2:Bool atom):Bool atom = case (v1,v2) of (B(b1),B(b2)) => B (b1 andalso b2) | _ => raise (Fail “type mismatch”)
TCS2002 32
Bounded Polymorphism (I)
• Extends both parametric polymorphism and subtyping . .()
TCS2002 33
• IntA IntC
Bounded Polymorphism (II)
• Example: Nat Int– double: Int.
• IntA IntA • where = IntA
– plus: Int.( )• where = IntA • plus (mkI 1, natToInt (mkN 2))
TCS2002 34
Bounded polymorphism (III)
• Limitations– Type variable bounds
• ..() • where = A and = A
– First-class polymorphism– Functional subtyping
• (1 2). 2
• 2C where = 1C 2A
TCS2002 35
Formalization (II)
let f1 = 11 . x:1. c1 x in
…let fn = nn . x:n. cn x in
[]
“safe” interface types