'a stack val push : 'a -> 'a stack -> 'a stack val pop : 'a stack -> (‘a * 'a stack) option end structure ListStack : STACK = struct type 'a stack = 'a list val empty = [] fun push x s = x::s fun pop (h::t) = SOME (h,t) | pop [] = NONE end Structures 13
'a stack val push : 'a -> 'a stack -> 'a stack val pop : 'a stack -> (‘a * 'a stack) option end structure ListStack : STACK = struct type 'a stack = 'a list val empty = [] fun push x s = x::s fun pop (h::t) = SOME (h,t) | pop [] = NONE end Structures 14
'a stack val push : 'a -> 'a stack -> 'a stack val pop : 'a stack -> (‘a * 'a stack) option end structure ListStack : STACK = struct type 'a stack = 'a list val empty = [] fun push x s = x::s fun pop (h::t) = SOME (h,t) | pop [] = NONE end Structures 15
'a stack val push : 'a -> 'a stack -> 'a stack val pop : 'a stack -> (‘a * 'a stack) option end structure ListStack : STACK = struct type 'a stack = 'a list val empty = [] fun push x s = x::s fun pop (h::t) = SOME (h,t) | pop [] = NONE end Structures 16
('a list * int) val empty = ([], 0) fun push x (st, sz) = (x::st, sz + 1) fun pop (x::st, sz) = SOME (x, (st, sz - 1)) | pop ([], _) = NONE fun size (st, sz) = sz end ! A More Efficient Implementation 23
('a list * int) val empty = ([], 0) fun push x (st, sz) = (x::st, sz + 1) fun pop (x::st, sz) = SOME (x, (st, sz - 1)) | pop ([], _) = NONE fun size (st, sz) = sz end ! A More Efficient Implementation 24
('a list * int) val empty = ([], 0) fun push x (st, sz) = (x::st, sz + 1) fun pop (x::st, sz) = SOME (x, (st, sz - 1)) | pop ([], _) = NONE fun size (st, sz) = sz end ! A More Efficient Implementation 25
Oper = Num of int | Plus | Mul | … type rpn = Oper list fun compute (c : int Stack.stack) (eqn : rpn) = case eqn of [] => Stack.pop c | ((Num i)::eqn) => compute (Stack.push i) eqn | (Plus::eqn) => if (Stack.size c) < 2 then … else (* pop 2 off stack, add, push *) … end 32
Parametricity, AKA “the abstraction theorem” Mitchell 1986: “Representation independence and data abstraction.” Application of relational parametricity. 34
be unaffected by changes to the internal representation of the ADT that are preserved by its operations.” https://wiki.mpi-sws.org/star/paramore M ~ M’ => for all F, F(M) ~ F(M’) ! 36
be unaffected by changes to the internal representation of the ADT that are preserved by its operations.” https://wiki.mpi-sws.org/star/paramore e.g. RPNCalculator(ListStack) and RPNCalculator(SizeListStack) should behave the same way. 37
SET = structure type elem = Elem.t type set = elem list […] end signature ORD = sig type t val eq : t -> t -> bool val less : t -> t -> bool end signature SET = sig type elem type set val empty : set val add : elem -> set -> set […] end
Game.tick s num_ticks of NONE => () | SOME s => (Game.render screen s; case SDL.pollevent () of NONE => (SDL.delay 0; loop s) | SOME e => Option.app loop (Game.handle_event e s)) 54
1988 stack : ∀t. ∃ s. [s ⋀ (t ⋀ s -> s) ⋀ (s -> (t ⋀ s) ⋁ ⊤)] sig type ‘a stack emp : stack push : ‘a * ‘a stack -> ‘a stack pop : ‘a -> (‘a * ‘a stack) option end c.f.: 58
types as existential quantification: No. ! In Standard ML: No. (Generative functors) In OCaml: Yes. (Applicative functors) Does S1.add 6 (S2.empty) type check?
= structure type t = int val eq = Int.eq val less = if random() then Int.less else Int.greater end ! structure S1 = Set (F ()) structure S2 = Set (F ())
from WG2.8 Meeting, 2007 Generative vs. Applicative Functors: 64 Unfortunately, accounting for the semantics of! applicative functors is hard!! ! (see “F-ing Modules” expanded version)
& strict code dependencies when “programming in the large.”! ! It can be enforced at the linguistic level through type structure ! rather than managed by ad-hoc build system conventions. Takeaway 68
- Abstract Types Have Existential Type! Mitchell - Representation Independence and Data Abstraction! Derek Dreyer’s thesis & papers! Further Reading Code for this talk: https://github.com/chrisamaphone/compose2015 @chrisamaphone 70