phantom types and subtyping

35
Phantom Types and Subtyping Matthew Fluet Riccardo Pucella Dept. of Computer Science Cornell University

Upload: akiva

Post on 21-Jan-2016

57 views

Category:

Documents


0 download

DESCRIPTION

Phantom Types and Subtyping. Matthew Fluet Riccardo Pucella Dept. of Computer Science Cornell University. The Setting (I). Modern strongly typed functional language Parametric polymorphism val id:    = fn x => x Type constraints val idInt: int  int = fn x => x - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Phantom Types and Subtyping

Phantom Types and Subtyping

Matthew Fluet Riccardo Pucella

Dept. of Computer ScienceCornell University

Page 2: Phantom Types and Subtyping

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

Page 3: Phantom Types and Subtyping

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

Page 4: Phantom Types and 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

Page 5: Phantom Types and Subtyping

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

Page 6: Phantom Types and Subtyping

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

Page 7: Phantom Types and Subtyping

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

Page 8: Phantom Types and Subtyping

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”

Page 9: Phantom Types and Subtyping

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

Page 10: Phantom Types and Subtyping

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

Page 11: Phantom Types and Subtyping

TCS2002 11

Contributions

• A general encoding of subtyping hierarchies into phantom types

• A formalization of one use of the phantom types technique

Page 12: Phantom Types and Subtyping

TCS2002 12

Outline

• A recipe for interfaces and implementations

• Encoding subtyping hierarchies• Bounded polymorphism• Formalization

Page 13: Phantom Types and Subtyping

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

Page 14: Phantom Types and Subtyping

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

Page 15: Phantom Types and Subtyping

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

Page 16: Phantom Types and Subtyping

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

Page 17: Phantom Types and Subtyping

TCS2002 17

Deriving the Interface (II)

• Use concrete encodings in all covariant type positions

• Use abstract encodings in most contravariant type positions

Page 18: Phantom Types and Subtyping

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

Page 19: Phantom Types and Subtyping

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

Page 20: Phantom Types and Subtyping

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

Page 21: Phantom Types and Subtyping

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

Page 22: Phantom Types and Subtyping

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

Page 23: Phantom Types and Subtyping

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

Page 24: Phantom Types and Subtyping

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 = {}

Page 25: Phantom Types and Subtyping

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

Page 26: Phantom Types and Subtyping

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

Page 27: Phantom Types and Subtyping

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

Page 28: Phantom Types and Subtyping

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

Page 29: Phantom Types and Subtyping

TCS2002 29

Page 30: Phantom Types and Subtyping

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”)

Page 31: Phantom Types and Subtyping

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”)

Page 32: Phantom Types and Subtyping

TCS2002 32

Bounded Polymorphism (I)

• Extends both parametric polymorphism and subtyping . .()

Page 33: Phantom Types 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))

Page 34: Phantom Types and Subtyping

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

Page 35: Phantom Types and Subtyping

TCS2002 35

Formalization (II)

let f1 = 11 . x:1. c1 x in

…let fn = nn . x:n. cn x in

[]

“safe” interface types