430 likes | 543 Views
Existential Types. Robert Harper Fall Semester, 2002. Data Abstraction. Client / implementer model: Implementer provides a set of operations (API) over a representation type . Client sees only the API, and not the representation. Crucial idea: a fixed , but unknown , type.
E N D
Existential Types Robert Harper Fall Semester, 2002
Data Abstraction • Client / implementer model: • Implementer provides a set of operations (API) over a representationtype. • Client sees only the API, and not the representation. • Crucial idea: a fixed, but unknown, type. • “Generic” type, or type variable. • Opaque: underlying representation hidden.
Data Abstraction signature INT_QUEUE = sigtype tval empty : tval insert : int * t -> tval remove : t -> int * t end
Data Abstraction structure IQ :> INT_QUEUE = structtype t = int listval empty = nilval insert = op ::fun remove q = let val x::qr = rev q in (x, rev qr) end end structure Client = struct… IQ.insert … IQ.remove … end
Representation Independence • Informally, implementation can be changed without affecting client. • No need to recompile, unless interface (API) changes. • Client behavior unchanged if new implementation “simulates” the old. • Formally, it’s a matter of parametricity. • “Client code must respect simulations.”
Data Abstraction and Modularity • Data abstraction is a form of modularity. • Implementation-on-interface dependency. • Representation independence ensures separability of client from implementer. • MacQueen 86 argues that there’s more to modularity than just this. • Data abstraction is fundamental. • Previous approaches are inadequate.
Type Theory for ADT’s • Mitchell-Plotkin proposes a formalism for data abstraction. • “Existential types”. • Consistent with CLU, original ML. • Reynolds and Girard anticipated them by about 10 years. • “Universal types”, parametricity. • But didn’t quite take the last step!
A Core Formalism • Types: • Terms:
A Core Formalism • Type variable contexts • Term variable contexts
A Core Formalism • Well-formed types • Well-formed terms
ADT’s as Existentials • An interface is an existential type: • An implementation is a package: • A client uses the ADT via abstype:
ADT’s as Existentials • Representation type is exposed to the implementation! • Operations have type ft1/tgt2. • Rep’n type is hidden from the client! • abstype t with x:{…} in …(client)… end • Client “sees” only type variable t, not its representation.
ADT’s as Existentials • Essentially, the client must be polymorphic in the rep’n type. • 8 t. { emp:t, ins: …, rem:… } !t • Client is generic wrt rep’n type. • Formally, data abstraction comes down to polymorphism. • Representation independence follows from parametricity.
ADT’s as Existentials • In CLU you can only open an explicitly given package! • No variable packages, no computed packages. • Hence no need for existential types per se. • Notation:abstype t iswith x:is M in N end
Universals • The 8! fragment has several names: • System F; • Second Order Typed Lambda Calculus; • Polymorphic Lambda Calculus • Reynolds introduced it as a theory of type polymorphism in PL’s. • Girard introduced it as a tool for proving the consistency of second-order logic.
Existentials • Mitchell and Plotkin formulated the 89! fragment in their study of ADT’s. • But existentials are definable in terms of universals! • Thus, data abstraction boils down to polymorphism. • This was Reynolds’s original contention.
Existentials as Universals • Definability of existentials: • Definability of packages:
Existentials as Universals • Definability of abstype: • Client code is a polymorphic function! • Type: 8 t t2!t, where t does not occur in t. • Systematic reduction to polymorphism. • Analyze via parametricity properties.
The Avoidance Problem • The client’s must avoid the abstract type t. • Result type cannot sensibly involve t. • Is it sensible to reveal the representation? • This is consistent with CLU and ML. • SML ‘90 got this wrong. • In CLU you could substitute the representation type for t, since it’s always explicitly given.
The Avoidance Problem • If the implementation is an explicit package (as it is in CLU), we could replace free t’s by their rep’n type, t. • Philosophically questionable, but harmless. • But what if the implementation is given by an effectful expression? • Cannot extract the rep’n, even if we wanted to!
The Avoidance Problem • In the presence of non-trivial type equality, the type checker must find an equivalent type not involving t. • Sufficient, but not necessary, to normalize the type of the client. • Can induce explosion in type size in the presence of higher kinds.
The Avoidance Problem • In the presence of subtyping the type checker must find the least supertype avoiding t. • Weaken the client type to avoid t. • In the “best possible” way. • But may be no such least supertype! • eg, with type definitions in signatures • Can have incomparable super-types avoiding t.
The Avoidance Problem • The avoidance problem precludes the existence of principal types! • Type checking is incomplete (O’Caml). • Compromises separate compilation. • This is a central problem for module system design. • Def of SML kludges this up using “names”.
Open vs Closed Scope • Existentials provide a closed-scope abstraction mechanism. • The type is held abstract within a specified scope. • Existentials provide no direct access to the abstract type (or its operations). • Not sensible in the presence of effects! • Repn(if moon-is-full then M1 else M2)
Open vs Closed Scope • In contrast ML provides an open scope abstraction mechanism. • S.t refers to the type component of S. • S.t is abstract if S is sealed with a signature that hides t. • With an open scope mechanism a type must be made intrinsically abstract. • Rather than held abstract in the scope of its use.
Open vs Closed Scope • MacQueen 86 argues cogently for an open-scope modularity mechanism. • Preservation of type identity . • “Scope-and-a-half” problem. • Dependency. • Existentials are therefore inadequate.
Preservation of Type Identity • In ML if S is an implementation of SIG, then S.t is an abstract type. • Multiple occurrences of S.t refer to the same unknown type. • If we open a package twice, we get two different abstract types! • Whether nested or disjoint. • A matter of a-conversion.
Scope-And-A-Half • Consider the structure declaration:structure S = struct structure T :> SIG = … val x : T.tendval y : S.T.t = S.x • Note that t declared in T has “two scopes”! • The “rest” of the structure S. • The “rest” of the program.
Scope-and-a-Half • If you try to use abstype you have two (bad) choices. • Open the package T within the rest of the structure, but not the rest of the program. • Open the package T in the rest of the program, but not the rest of the structure. • There is no way to paraphrase in terms of abstype!
Dependency • Consider the functor declaration:functor F(S:>SIG) :> sig val x : S.t end = struct val x : S.t end • The type S.t has “two scopes”: • The result signature of F. • The body of F.
Dependency • There is no way to paraphrase this in terms of abstype! • One scope is a type. • The other scope is a term. • Open scope abstraction solves this problem. • Motivated interest in dependent types, but this ignored abstraction.
Dot Notation • Cardelli-Leroy study dot notation for existentials. • Introduce rep(M) and opns(M), where M is a package. • rep(M) is a type, opns(M) is a value. • When is this sensible? • Whenever it can be re-written using abstype as the sole elim form.
Dot Notation • The fundamental problem is a form of dependency. • M.t is a type. • M is a (fully general) package. • Type equality depends on term equality. • This is absent in the pure existential formalism! • This suggests that the dot notation is, in general, more powerful.
Dot Notation • When is dot notation eliminable? • Essentially, whenever the package is explicitly bound to a variable. • The binding site of the variable is the point at which the package is opened. • Does not address the dependency problem. • Motivated translucent sums, manifest types.
First Class vs Second Class • Existentials provide first-class ADT’s. • An existential type is “just” a type. • Can compute packages conditionally on run-time state. • Can store packages in data structures, reference cells. • Mitchell-Plotkin argue that this is good. • eg, dynamic selection of implementation
First Class vs Second Class • But this implies that the type theory must be relatively weak. • S.t is not well-defined unless S is a value. • Bad: (if moon-is-full then M1 else M2).t • If modules are second class, such examples do not arise! • M.t cannot be ill-defined due to run-time effects! • Assuming no conditional module expressions.
First Class vs Second Class • In the higher-order case we must assume the worst: • (9 t.t)!(9 t.t) may compute type part of result in terms of value part of argument. • fewer type sharing equations! • Perversely, O’Caml uses a first-class type system for second-class modules! • Weaker than necessary. • Almost works in the first-order case (avoidance problem is not solved).
Summary • Existentials model data abstraction through a closed-scope mechanism. • Provides a rigorous foundation for representation independence. • Clear connections with classical PL’s. • But existentials are inadequate for flexible, expressive modularity.