existential types and module systems xiaoheng ji department of computing and software march 19, 2004

67
Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Upload: posy-loren-park

Post on 02-Jan-2016

217 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Existential Types and Module Systems

Xiaoheng Ji

Department of Computing and Software

March 19, 2004

Page 2: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Data Abstraction The fundamental idea: the separation of the client

from the implementor of the abstraction by an interface.

The central ideas:– Define a representation type together with operations that

manipulate values of that type.– Hold the type representation type abstract from client of the

ADT to ensure representation independence. ``Abstract types have existential type''

[Mitchell and Plotkin 84] Existential types provide the fundamental linguistic

mechanisms for defining interfaces, implementing them, and using the implementation in client code.

Existential types are a kind of type originally developed in constructive logic.

Page 3: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Existential Types

Two different ways of looking at an existential type, written {X, T} or X.T– logical intuition: an element of {X, T} is a value of

type [X=>S]T, for some type S.– Operational intuition: an element of {X, T} is a

pair, written {*S, t}, of a type S and a term t of type [XS]T.

Instead of X.T, the nonstandard notation {X, T} suggests that the existential value is a mixed type-value tuple.

Page 4: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Existential Types - Overview

Typing and evaluation rules Introducing ADTs

– Introducing ADTs– Introducing objects– Objects vs. ADTs

Encoding existential types

Page 5: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Existential introduction

an existentially typed value introduced by pairing a type with a term: {*S,t}

intuition: value {*S,t} of type {X,T} is a module with a type component S and a term component t, where [S/X]T.

The type S is often called the hidden representation type, or sometimes the witness type of the package.

Page 6: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Example

p = {*Nat, {a = 0, f = x : Int.succ(x)}} as {∃X.{a : X, f : X->X}};

p : {∃X.{a : X, f : X->X}}

q = {*Nat, {a = 0, f = x : Int.succ(x)}} as {∃X.{a : X, f : X->Nat}};

q : {∃X.{a : X, f : X->X}}

Nat – type component

{a = 0, f = x : Int.succ(x)} – term componentas {∃X.{a : X, f : XNat}} – type annotation

Page 7: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Typing rule for existential introduction

A value of type X.T is a package with a witness type T‘ for X and a value term t : [X -> T']T.– pack X = T' with t as T : X.T (conventional notation)– {*T', t} as {X, T} (Pierce's notation)

The Introduction typing rule for existential types:

|- t1 : [X => U]T1

|- {*U, t1} as {X, T1} : {X, T1}(T- PACK)

Page 8: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Examples of Existential types

{*Nat, 0} as {X, X} : {X, X}

{*Bool, true} as {X, X} : {X, X}

p = {*Nat, {a = 0, f = x : Nat. succ x}} as {X, {a : X, f : X -> Nat}}

p : {X, {a : X, f : X -> Nat}}

q = {*Bool, {a = true, f = x : Bool. 0}} as {X, {a : X, f : X -> Nat}}

q : {X, {a : X, f : X -> Nat}}

The type part is hidden (opaque, abstract), and the value

part provides an interface for interpreting the hidden type.

Page 9: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Unpacking existential values

Unpacking an existential: let {X,x} = t1 in t2

Type variable X cannot occur in T2 -- it is not in scope (i.e. doesn't appear in the context ). This means that the name X of the existential witness type cannot "escape“ from the let expression.

Also, within the body t2, the type X is abstract and can only be used through the interface provided by x : T1.

|- t1 : {X, T1}

|- let {X,x} = t1 in t2 : T2

(T- UNPACK), X, x: T1 |- t2 : T2

Page 10: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Example

p = {*Nat, {a = 0, f = x : Nat. succ x}} as {X, {a : X, f : X -> Nat}}

p : {X, {a : X, f : X -> Nat}}

The elimination expression

let {X, x} = p in (x.f x.a);⊳ 1 : Natopens p and uses the fields of its body (x.f and x.a) to compute a numeric result.

Page 11: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Example

p = {*Nat, {a = 0, f = x : Nat. succ x}} as {X, {a : X, f : X -> Nat}}

p : {X, {a : X, f : X -> Nat}}

The body of the elimination form can also involve the type variable x:

let {X, x} = p in (y:X. (x.f y)) x.a;⊳ 1 : NatThe fact that the package’s representation type is held abstract during the typechecking of the body means that the only operations allowed on x are those warranted by its ``abstract type'‘ {a : X, f : X->Nat}.

Page 12: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Example

p = {*Nat, {a = 0, f = x : Nat. succ x}} as {X, {a : X, f : X -> Nat}}

p : {X, {a : X, f : X -> Nat}}

all operations on term x must be warranted by its abstract type, e.g. we cannot use x.a concretely as a number (since the concrete type of the module is hidden):

let {X, x} = p in succ(x.a);⊳ Error: argument of succ is not a number

Page 13: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Examplep = {*Nat, {a = 0, f = x : Nat. succ x}} as {X, {a : X, f : X -> Nat}}

p : {X, {a : X, f : X -> Nat}}

In the rule T-UNPACK, the type variable X appears in the context in which t2’s type is calculated, but does not appear in the context of the rule’s conclusion. This means that the result type T2 cannot contain X free, since any free occurrences of X will be out of scope in the conclusion.

let {X, x} = p in x.a;⊳ Error: Scoping error!

must add side condition to typing rule for existential elimination;

X may not occur in the result type

Page 14: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Evaluation rule for existentials

let {X, x} = ({*T11, v12} as T1) in t2 [X->T11] [x->v12] t2

(E- UNPACKPACK)

If the first subexpression of the let has already been reduced to a concrete package, then we may substitute the components of this package for the variables X and x in the body t2.

In terms of analogy with modules, this rule can be viewed as a linking step, in which symbolic names (X and x) referring to the components of a separately compiled module are replaced by the actual contents of the module.

Since the type variable X is substituted away by this rule, the resulting program actually has concrete access to the package’s internals.

Page 15: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Existential Types - Overview

Typing and evaluation rules Introducing ADTs

– Introducing ADTs– Introducing objects– Objects vs. ADTs

Encoding existential types

Page 16: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Parametricity

consider:

p = {*Nat, {a = 0, f = x : Nat. 0}} as {X, {a : X, f : X -> Nat}}

q = {*Bool, {a = false, f = x : Bool. 0}} as {X, {a : X, f : X -> Nat}} evaluation does not depend on the specific type of p and q: it is

parametric in X:

let {X, x} = p in (x.f x.a);⊳ 0let {X, x} = q in (x.f x.a);⊳ 0

Idea: use parametricity to construct two kinds of programmer defined abstractions: abstract data types (ADTs) and objects

Page 17: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Existential Types - Overview

Typing and evaluation rules Introducing ADTs

– Introducing ADTs– Introducing objects– Objects vs. ADTs

Encoding existential types

Page 18: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Abstract Data Types

A conventional abstract data type (or ADT) consists of: A type name A A concrete representation type T Implementations of some operations for creating, querying, and

manipulating values of type T An abstraction boundary enclosing the representation and

operations

Inside this boundary, elements of the type are viewed concretely (with type T).

Outside, they are viewed abstractly, with type A. Values of type A may be passed around, stored in data structures, etc., but not directly examined or changed – the only operations allowed on A are those provided by the ADT.

Page 19: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Example

signature COUNTER =

sig

type counter

val new : counter

val get : counter -> Nat

val inc : counter -> counter

end;

abstract representation type

concrete representation type

interface

implementation

structure Counter :> COUNTER =

struct

type counter = Nat

val new = 1

fun get(n) = n

fun inc(n) = n + 1

end;

Page 20: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Example

signature COUNTER =

sig

type counter

val new : counter

val get : counter -> Nat

val inc : counter -> counter

end;

structure Counter :> COUNTER =

struct

type counter = Nat

val new = 1

fun get(n) = n

fun inc(n) = n + 1

end;

- counter.get ( counter.inc counter.new);

val it = 2 : Nat

Page 21: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

ADTs as existentialssignature COUNTER =

sig

type counter

val new : counter

val get : counter -> Nat

val inc : counter -> counter

end;

structure Counter :> COUNTER =

struct

type counter = Nat

val new = 1

fun get(n) = n

fun inc(n) = n + 1

end;

COUNTER =

Counter.{

new : Counter,

get : Counter -> Nat,

inc : Counter -> Counter}

counterADT =

{*Nat, {

new = 1,

get(n) = n : Nat. n

inc(n) = n : Nat. succ(n) }}

As COUNTER ``Abstract types have existential type'‘ [Mitchell and Plotkin 84]

Page 22: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

ADTs as existentials

COUNTER =

Counter.{

new : Counter,

get : Counter -> Nat,

inc : Counter -> Counter

CounterADT =

{*Nat, {

new = 1,

get(n) = n : Nat. n

inc(n) = n : Nat. succ(n) }}

As COUNTER

type name Counter can be used just like a new base type e.g. we can define new ADTs with representation type Counter,

e.g. a FlipFlop

let {Counter, counter} = CounterADT in

counter.get ( counter.inc counter.new);

⊳ 2 : Nat

Page 23: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Flip-FlopLet {Counter, counter} = counterADT in

Let {FlipFlop, flipflop} = {*Counter,

{new = counter.new,read = c : Counter. iseven (counter.get c),toggle = c : Counter. counter.inc c,reset = c : Counter. counter.new}}

As {FlipFlop,{new: FlipFlop, read: FlipFlop->Bool,toggle: FlipFlop->FlipFlop, reset: FlipFlop->FlipFlop}} in

flipflop.read (flipflop.toggle (flipflop.toggle flipflop.new));⊳ false : Bool

Page 24: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Representation independence

Alternative implementation of the CounterADT: counterADT =

{*{x:Nat}, {new = {x=1}, get = i : {x:Nat}. i.x, inc = i : {x:Nat}. {x=succ(i, x)}}}

as {Counter,{new: Counter, get: Counter->Nat, inc: Counter-

>Counter}}; ⊳counterADT : {Counter,

{new: Counter, get: Counter->Nat, inc: Counter->Counter}}

Representation independence: follows from parametricity: the whole program remains typesafe since the counter instances cannot be accessed except using ADT operations

Page 25: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

ADT-style of programming

yields huge improvements in robustness and maintainability of large systems:– limits the scope of changes to the program– encourages the programmer to limit the

dependencies between parts of the program (by making the signatures of the ADTs as small as possible)

– forces programmers to think about designing abstractions

Page 26: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Existential Types - Overview

Typing and evaluation rules Introducing ADTs

– Introducing ADTs– Introducing objects– Objects vs. ADTs

Encoding existential types

Page 27: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Existential objects

two basic components: internal state, methods to manipulate the state.

e.g., a counter object holding the value 5 might be written c = {*Nat,

{state = 5, methods = {get = x : Nat. x,

inc = x : Nat. succ(x)}}} as Counter;

where: Counter = {X, {state:X, methods: {get: X->Nat, inc: X-

>X}}};

Page 28: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Invoking the get method

c = {*Nat, {state = 5,

methods = {get = x : Nat. x, inc = x : Nat. succ(x)}}}

as X, { state: X, methods: { get: X->Nat,

inc: X->X}}

let {X, body} = c in body.methods.get (body.state);⊳ 5: Nat

Page 29: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Encapsulating the get method

C = X, { state: X, methods: { get: X->Nat,

inc: X->X}}

sendget = c: Counter.let {X, body} = c inbody.methods.get (body.state);

⊳ sendget : Counter -> Nat

Page 30: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Invoking the inc method c = {*Nat,

{state = 5, methods = {get = x : Nat. x,

inc = x : Nat. succ(x)}}}as X. { state: X, methods: { get: X->Nat,

inc: X->X}}

let {X, body} = c in body.methods.inc (body.state);

⊳ Error: scoping errorWhy? X appears free in the body of the let

Page 31: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Encapsulating the inc method in order to properly invoke the inc method, we must repackage

the fresh internal state as a counter object:

c1 = let {X, body} = c in {*X, {state = body.methods.inc (body.state), methods = body.methods}} as Counter;

sendinc = c : Counter. let {X, body} = c in

{*X, {state = body.methods.inc (body.state), methods = body.methods}} as Counter;

⊳ sendinc : Counter -> Counter

Page 32: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Example

More complex operations on counters can be implemented in terms of these two basic operations:

add = c : Counter. sendinc (sendinc (sendinc c)); add : Counter -> Counter

Page 33: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Existential Types - Overview

Typing and evaluation rules Introducing ADTs

– Introducing ADTs– Introducing objects– Objects vs. ADTs

Encoding existential types

Page 34: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Abstract type of counters

ADT-style: counter values are elements of the underlying representation (i.e. simple numbers of type Nat)

object-style: each counter is a whole module, including not only the internal representation but also the methods. Type Counter stands for the whole existential type:

X. { state: X, methods: { get: X->Nat,

inc: X->X}}

Page 35: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Stylistic advantages

advantage of the object-style: since each object chooses its own representation and operations, different implementations of the same object can be freely intermixed

advantage of the ADT-style: binary operations (i.e. operations that accept >= 2 arguments of the abstract type) can be implemented, contrary to objects

Page 36: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Binary operations and the object-style

e.g. set objects type:NatSet = {X, {state: X, methods: {empty: X,

singleton: Nat->X, member: X->Nat-

>Bool, union: X->NatSet-

>X}}} cannot implement the method since it can have no

access to the concrete representation of the second argument

in reality, mainstream OO languages such as C++ and Java have a hybrid object model that allows binary operations (with the cost of restricting type equivalence)

Page 37: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Existential Types - Overview

Typing and evaluation rules Introducing ADTs

– Introducing ADTs– Introducing objects– Objects vs. ADTs

Encoding existential types

Page 38: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Duality

universal types: ∀X.T is a value of type [S/X]T for all types S.

existential types: ∃X.T is a value of type [S/X]T for some type S.

idea: exploit duality to encode existential types using universal types, using the equality:

∃X.T = ¬∀X.¬T def

Page 39: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Encoding

encoding existential types using universal types:

{∃X,T} = ∀Y. (∀X. T->Y) -> Y. operational view: a module is a value that gets a

result type and a continuation, then calls the continuation to yield the final result

A continuation is something that we can call and forget about because it does not return control to the caller.

def

Page 40: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Encoding existential elimination

given:

let {X, x} = t1 in t2

where t1: Y. ( X. T∀ ∀ ->Y) -> Y first apply to result type T2 to get type ( X. T∀ -> >T2) ->

T2:

let {X, x} = t1 in t2 = t1 T2… then apply to continuation of type X. T∀ ->T2 to get result

type T2:

let {X, x} = t1 in t2 = t1 T2 (X. x : T.t2)

def

def

Page 41: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Encoding existential introduction

given:

{*S, t} as {∃X, T}

we must use S and t to build a value of type Y. ( X. T ∀ ∀ -> Y) -> Y begin with two abstractions:

{*S, t} as {∃X, T} = Y. f ( X. T∀ ->Y) … apply f to appropriate arguments: first, supply S:

{*S, t} as {∃X, T} = Y. f ( X. T∀ ->Y). f S … then supply t of type S to get result type Y:

{*S, t} as {∃X, T} = Y. f ( X. T∀ ->Y). f S t

def

def

def

Page 42: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Existential types - summary

existential types are another form of polymorphism parametricity of existentials leads to representation

independence trade-offs between ADTs and objects:

– ADTs support binary operations, objects do not– objects support free intermixing of different implementations,

ADTs do not existentials can be encoded using universal types

Page 43: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Module systems - overview

The OCaml Module System Haskell Modules

Page 44: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

The OCaml Module System

three key parts: structures: are packaged environments. They consist of a group

of core ML objects (values, types, exceptions) and can be manipulated as a single unit.

module Name = struct implementation end signatures: are the ``types’’ of structures. Corresponding to

each structure one can derive a signature which consists of the names of the objects in the structure and type information for the objects.

module type Name = sig signature end functors: are mappings taking structures to structures. These

are useful in specifying ``generic’’ packages that given any structure with a given signature can generate a new structure with some other signature.

module Name = functor (ArgName : ArgSig) -> struct implementation end

module Name (Arg : ArgSig) = struct implementation end

Page 45: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

OCaml – Modules: Values

Collection of named (mutually-recursive) values:module IntListSet = struct let empty = [] let add = fun i is -> i ::

List.filter (fun j -> i != j) is let asList = fun is -> isend

Access using dot-notation:val ok : int listlet ok = IntListSet.add 1 IntListSet.empty

Modules (structures) have types (signatures):module type INTLISTSET = sig val empty : int list val add : int -> int list -> int list val asList : int list -> int listend

Page 46: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

OCaml – Modules: Types

May also include named (data) types:module IntListSet = struct type t = int list

let empty = [] let add = fun i is -> i ::

List.filter (fun j -> i != j) is let asList = fun is -> isend

val ok : IntListSet.tlet ok = IntListSet.add 1 IntListSet.empty

And named (nested) modules.

Page 47: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

OCaml – Modules: Abstract Types May also include abstract types:

module type INTSET = sig type t --- abstract val empty : t

val add : int -> t -> t val asList : t -> int listendmodule IntSet : INTSET = struct type t = int list --- concrete let empty = [] let add = fun i is -> i ::

List.filter (fun j -> i != j) is let asList = fun is -> isendval ok : IntListSet.tlet ok = IntListSet.add 1 IntListSet.emptyval fail : int listlet fail = IntListSet.add 1 [2]-- type error: incompatible types int list and IntListSet.t

Page 48: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

OCaml – Modules: Separate Compilation

Top-level modules may correspond with compilation units. True separate compilation is possible if each top-level module is

accompanied by a top-level signature. intSet.mli:

type tval empty : tval add : int -> t -> tval asList : t -> int list

intSet.ml:type t = int listlet empty = []let add = fun i is -> i ::

List.filter (fun j -> i != j) islet asList = fun is -> is

Compiler never needs to look at intSet.ml when compiling other modules – only intSet.mli.

Page 49: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

OCaml – Modules: Value Parameterisation

May be parameterised by the values within other modules(functors):

module type INTORD = sig val less : int -> int -> boolendmodule MkIntBinTreeSet = functor (IntOrd : INTORD) ->struct type t = Leaf | Node of bintree * int * bintree let empty = Leaf let rec add = fun i t -> match t with | Leaf -> Node (Leaf, i, Leaf) | Node (l, j, r) ->

if IntOrd.less i j then Node (add i l, j, r)else ...

let rec asList = …end

module IntOrd = struct let less = (<) endmodule IntBinTreeSet = MkIntBinTreeSet IntOrd

Page 50: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

OCaml – Modules: Type Parameterisation

May be parameterised by the types within other modules:

module type ORD = sig type t val less : t -> t -> boolend

module MkBinTreeSet = functor (Ord : ORD) -> struct type t = Leaf | Node of bintree * Ord.t *

bintree let empty = Leaf let rec add = fun x t -> … let rec asList = …end

Module IntOrd = struct type t = int let less = (<)endModule IntBinTreeSet = MkBinTreeSet IntOrd

Page 51: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

OCaml – Modules: Type Sharing Interaction of

– Separate compilation, and– Parameterisation by types

Requires special support m.mli:

module type SET = sigtype elttype tval empty : tval add : elt -> t -> tval asList : t -> elt list

end

module type MKBINTREESET = functor (Ord : ORD) -> SET

module MkBinTreeSet : MKBINTREESET

Page 52: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

OCaml - Modules: Type Sharing Interaction of

– Separate compilation, and– Parameterisation by types

Requires special support x.ml:

module IntOrd = struct type t = int let less = (<)

end module IntBinTreeSet = M.MkBinTreeSet IntOrd

val fail : IntBinTreeSet.t let fail = IntBinTreeSet.add 1 IntBinTreeSet.empty

-- type error: incompatible types int and IntBinTreeSet.elt

Page 53: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

OCaml – Modules: Type Sharing (2)

We need to capture the sharing of types between the argument and result of the functor MkBinTreeSet.

m.mli: module type SET = sig

type elttype tval empty : tval add : elt -> t -> tval asList : t -> elt list

end

module type MKBINTREESET = functor (Ord : ORD) -> (SET with type elt =

Ord.t)

module MkBinTreeSet : MKBINTREESET

Page 54: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

OCaml – Modules: Type Sharing (2)

We need to capture the sharing of types between the argument and result of the functor MkBinTreeSet.

x.ml:

module IntOrd = struct

type t = int

let less = (<)

end

module IntBinTreeSet = M.MkBinTreeSet IntOrd

val fail : IntBinTreeSet.t

let fail = IntBinTreeSet.add 1 IntBinTreeSet.empty

-- ok, since int = IntOrd.t = IntBinTreeSet.elt

Page 55: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

The Haskell module system

``Haskell has a very simple module system -- a flat module namespace with the ability to import and export various entities, hide names, and specify that module qualification (M.x) is required. It also provides support for abstract data types by allowing one to export a data type without its constructors. ’’

-- Paul Hudak, 1998

Page 56: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

The Haskell Module System

Module headersmodule Ant where

data Ants = …anteater x = …

The convention for file names is that a module Ant resides in the Haskell file Ant.hs or Ant.lhs.

Page 57: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

The Haskell Module System

Importing a modulemodule Bee where

import Ant

beeKeeper = …

This means that the visible definitions from Ant can be used in Bee. By default the visible definitions in a module are those which appear in the module itself

module Cow where

import Bee

Page 58: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

The Haskell Module System

The main module

Each system of modules should contain a top-level module called Main, which gives a definition to the name main.

Note that a module with no explicit name is treated as Main.

Page 59: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

The Haskell Module System

Export controls– we might wish not to export some auxiliary functions.– We perhaps want to export some of the definitions we

imported from other modules.

We can control what is exported by following the name of the module with a list of what is to be exported:module Bee ( beeKeeper, Ants(..), anteater ) where …

We follow the type name with (..) to indicate that the constructors of the type are exported with the type itself; if this is omitted, then the type acts like an abstract data type.

Page 60: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

The Haskell Module System

We can also state that all the definitions in a module are to be exported:module Bee ( beeKeeper, module Ant ) where …

or equivalentlymodule Bee ( module Bee, module Ant ) where …

The simpler headermodule Fish where

is equivalent to module Fish ( module Fish ) where

Page 61: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

The Haskell Module System

Import controls

we can control how objects are to be imported:import Ant ( Ants(..) )

stating that we want just the type Ants

we can alternatively say which names we wish to hide:

import Ant hiding ( anteater )

Page 62: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

The Haskell Module System

Suppose that in our module we have a definition of bear, and also there is an object named bear in the module Ant.– Use the qualified name Ant.bear for the imported object,

reserving bear for the locally defined one.

To use qualified names we should make the import thus:import qualified Ant

Give a local name Insect to a imported module Ant:import Insect as Ant

Page 63: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

The Haskell Module System

The standard prelude– Prelude.hs is implicitly imported into every module.– Modify this import, perhaps hiding one or more bindings thus:

module Eagle whereimport Prelude hiding (words)

– A re-definition of a prelude function cannot be done ``invisibly’’. We have explicitly to hide the import of the name that we want to re-define.

– If we also wish to have access to the original definition of words we can make a qualified import of the prelude,import qualified Prelude

and use the original words by writing its qualified name Prelude.words.

Page 64: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Example - Stacks

A stack implemented as an ADT module can be defined as follows: module Stack(Stack,push,pop,top,emptyStack,stackEmpty) where

push :: a -> stack a -> Stack a

pop :: Stack a -> Stack a

top :: Stack a -> a

emptyStack :: Stack a

stackEmpty :: Stack a -> Bool

Page 65: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Example - Stacks

A first implementation can be done using a user-defined type:data Stack a = EmptyStk

| Stk a (Stack a)

push x s = Stk x s

pop EmptyStk = error ``pop from an empty stack’’

pop (Stk _ s) = s

top EmptyStk = error ``top from an empty stack’’

top (Stk x _) = x

emptyStack = EmptyStk

stackEmpty EmptyStk = True

stackEmpty _ = False

Page 66: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Example - Stacks

Another possible implementation can be obtained using the predefined list data structure because push and pop are similar to the (:) and tail operations. newtype Stack a = Stk [a]

push x (Stk xs) = Stk (x:xs)

pop (Stk []) = error ``pop from an empty stack’’

pop (Stk (_:xs)) = Stk xs

top (Stk []) = error ``top from an empty stack’’

top (Stk (x:_)) = x

emptyStack = Stk []

stackEmpty (Stk []) = True

stackEmpty (Stk _ ) = False

Page 67: Existential Types and Module Systems Xiaoheng Ji Department of Computing and Software March 19, 2004

Thank You Very Much!

References: Types and Programming Languages Abstract Types Have Existential Type First-Class Modules for Haskell