Existential Types and Type Manipulation in Programming Languages

 
Existential Types
 
Sijmen van Bommel & Quinten Kock
based on Types and Programming Languages
(Pierce 2002)
 
Recap: Universal types
 
Polymorphism
X.T: value has type T[X:=S] for any type S
type S is abstract: only known after specialization
id = λX. λx:X. x
 
:
 
X. X->X
erases to untyped λx. x
specializes to λx:S. x :
 
S->S
 
Existential types
 
{
X
,
 
T
}
:
 
v
a
l
u
e
 
h
a
s
 
t
y
p
e
 
T
[
X
:
=
S
]
 
f
o
r
 
s
o
m
e
 
t
y
p
e
 
X
value can be seen as pair {*S, t}: a type S and a term t : T[X:=S]
type S is hidden: only visible in the definition of t
 
Type abstraction
 
Different hidden types, same existential type
p : {
X, {a:X, f:X->Nat}}
A.
p = {*Nat, {a=0, f=\x:Nat. x}}
B.
p = {*Bool, {a=False, f=\x:Bool. if x then 1 else 0}}
 
Type ambiguity
 
Same value, different existential type
p = {*Nat, {v=0, f=λx:Nat. succ(x)}}
A.
p : {
X, {v:X, f:X->X}}
B.
p : {
X, {v:X, f:X->Nat}}
C.
p : {
X, {v:X, f:Nat->Nat}}
D.
p : {
X, {v:Nat, f:Nat->Nat}}
 
Packing
 
p
a
c
k
i
n
g
 
a
 
v
a
l
u
e
 
c
a
n
 
b
e
 
d
o
n
e
 
u
s
i
n
g
 
a
s
{*T,t} as U
 
where T is some type
t is a term/value
U is an existential type
 
Examples:
{
*
N
a
t
,
 
4
2
}
 
a
s
 
{
X
,
 
X
}
{*Bool, true} as {
X, X
}
 
Unpacking
 
example:
p = {*Nat, {a: 42, get: λn:Nat.n} as {
X, {X, X -> Nat}}
let {X,x} = p in x.get(x.a)
evaluates to?
 
where p is an existentially typed value
X becomes a type
x becomes a term/value
v is a term/value that may contain x
 
U
n
p
a
c
k
i
n
g
 
c
a
n
 
b
e
 
d
o
n
e
 
u
s
i
n
g
 
l
e
t
 
 
=
 
 
i
n
 
let {X,x} = p in v
 
Illegal unpacking
 
p = {*Nat, {a: 42, get: λn:Nat.n} as {
X, {X, X -> Nat}}
let {X,x} = p in succ(x.a)
     
argument of succ
is X, not a number
let {X,x} = p in x.a
       
type X
escapes scope
 
Syntax
 
terms: ...
 
| {*T, t} as U
      
packing
 
| let {X,x}=p in v
     
unpacking
 
values: ...
 
| {*T,v} as U
      
package
value
 
types: ...
 
| {
X, T}
       
existential
type
 
Evaluation rules
 
let {X,x} = ({*T,t} as U) in e → e[X := T, x := t]
 
t1 → t2
{*T, t1} as U → {*T, t2} as U
 
t1 → t2
let {X,x} = t1 in e → 
let {X,x} = t2 in e
 
E-UnpackPack
 
E-Pack
 
E-Unpack
 
Typing rules
 
Γ 
 t : T[ X := U ]
Γ 
 {*U, t} as {
X, T
} : {
X, T
}
 
Γ 
 t1 : {
X, T
}       
Γ, X, x : T 
 y : Y
Γ 
 let {
X, x
} = t1 in y : Y
 
T-Pack
 
T-Unpack
 
Abstract Data Types (ADTs)
 
Existential types hide actual representation
Useful for enforcing abstraction boundaries
let {X,x}=p in (λy:X. x.f y) x.a
x.f and x.a are values from p.
X is abstract for Nat, but:
let {X,x}=p in succ(x.a)
is forbidden! We are not allowed to use values of type X as Nat outside of p.
ADTs are like modules:
let {X,x}=p 
 import p
 
ADT examples
 
Counter
counterADT = {*Nat, {new=0, get=λi:Nat. i, inc=λi:Nat. succ(i)}}
as {
C, new:C, get: C->Nat, inc: C->C}
Prevents incorrect use (like dec)
Associative datatypes
abstract over hashmap vs treemap vs ...
maintain invariants (e.g. that the tree is in order)
Rational numbers
Floating point vs fixed point vs ratio
 
Existential Objects
 
counterObject 
= {*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} = counterObject in body.methods.get(body.state)             
evaluates
to?
 
functions using counters 
(as existential objects)
 
sendinc = 
λc : Counter .
   
let {X,Body} = c in
    
{*X,
     
{state =
body.methods.inc(body.state).
     
 methods = body.methods}}
    
as Counter
 
sendinc : {
X, {state:X, ...}} → {
X, {state:X, ...}}
 
ADTs
 
usage: 
counter.get( counter.inc( counter.new))
type: {
C, {get: C-> Nat, ...}}
 
uses internal representation
set of available functions unextendable
full support for binary operators
 
usage: 
sendget (sendinc (counterObject))
type: {
C, {state: C, methods: {get:C->Nat, ...}}}
 
keeps packaged structure
set of available functions can be extended
limited support for binary operators
 
modern object oriented languages use a hybrid.
 
Objects
 
vs
 
Encoding existential types as universal types 
(with example)
 
existential type: 
 
{
C, {get: C->Nat, ...}}
universal type:
 
Y. (
C. {get: C->Nat, ...} -> Y) -> Y
 
existential value:
 
{*Nat, {get=id, ...}}
universal value:
 
λ
Y.λy:(
C. {get: C->Nat, ...} -> Y).
      
y[Nat]({get=id,
...})
 
existential usage:
 
let (Counter, counter) = p in v
universal usage:
 
p[V](λCounter. λcounter. v)
Slide Note

3 + 2 + 2 + 2 + 2 + 2 + 2 + 1 + 4 + 3 + 2 + 2 + 2.5 + 1.5 + 4 + 6 = 41 min

Embed
Share

Explore the concepts of existential types, type abstraction, type ambiguity, packing, and unpacking in the context of programming languages. Learn how to work with hidden types, universal types, and the nuances of type manipulation. Examples and illustrations are provided to enhance understanding.

  • Existential Types
  • Type Abstraction
  • Type Manipulation
  • Programming Languages

Uploaded on Sep 12, 2024 | 1 Views


Download Presentation

Please find below an Image/Link to download the presentation.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. Download presentation by click this link. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.

E N D

Presentation Transcript


  1. Existential Types Sijmen van Bommel & Quinten Kock based on Types and Programming Languages (Pierce 2002)

  2. Recap: Universal types Polymorphism X.T: value has type T[X:=S] for any type S type S is abstract: only known after specialization id = X. x:X. x : erases to untyped x. x specializes to x:S. x :S->S X. X->X

  3. Existential types { X, T}: value has type T[X:=S] for some type X value can be seen as pair {*S, t}: a type S and a term t : T[X:=S] type S is hidden: only visible in the definition of t

  4. Type abstraction Different hidden types, same existential type p : { X, {a:X, f:X->Nat}} A. p = {*Nat, {a=0, f=\x:Nat. x}} B. p = {*Bool, {a=False, f=\x:Bool. if x then 1 else 0}}

  5. Type ambiguity Same value, different existential type p = {*Nat, {v=0, f= x:Nat. succ(x)}} A. p : { X, {v:X, f:X->X}} B. p : { X, {v:X, f:X->Nat}} C. p : { X, {v:X, f:Nat->Nat}} D. p : { X, {v:Nat, f:Nat->Nat}}

  6. Packing packing a value can be done using as where T is some type t is a term/value U is an existential type {*T,t} as U Examples: {*Nat, 42} as { X, X} {*Bool, true} as { X, X}

  7. Unpacking Unpacking can be done using let = in where p is an existentially typed value let {X,x} = p in v X becomes a type x becomes a term/value v is a term/value that may contain x example: p = {*Nat, {a: 42, get: n:Nat.n} as { X, {X, X -> Nat}} let {X,x} = p in x.get(x.a) evaluates to?

  8. Illegal unpacking p = {*Nat, {a: 42, get: n:Nat.n} as { X, {X, X -> Nat}} let {X,x} = p in succ(x.a) is X, not a number argument of succ let {X,x} = p in x.a escapes scope type X

  9. Syntax terms: ... | {*T, t} as U | let {X,x}=p in v packing unpacking values: ... | {*T,v} as U package value types: ... | { X, T} existential type

  10. Evaluation rules E-UnpackPack let {X,x} = ({*T,t} as U) in e e[X := T, x := t] t1 t2 E-Pack {*T, t1} as U {*T, t2} as U t1 t2 E-Unpack let {X,x} = t1 in e let {X,x} = t2 in e

  11. Typing rules t : T[ X := U ] T-Pack {*U, t} as { X, T} : { X, T} t1 : { X, T} , X, x : T y : Y T-Unpack let {X, x} = t1 in y : Y

  12. Abstract Data Types (ADTs) Existential types hide actual representation Useful for enforcing abstraction boundaries let {X,x}=p in ( y:X. x.f y) x.a x.f and x.a are values from p. X is abstract for Nat, but: let {X,x}=p in succ(x.a) is forbidden! We are not allowed to use values of type X as Nat outside of p. ADTs are like modules: let {X,x}=p import p

  13. ADT examples Counter counterADT = {*Nat, {new=0, get= i:Nat. i, inc= i:Nat. succ(i)}} as { C, new:C, get: C->Nat, inc: C->C} Prevents incorrect use (like dec) Associative datatypes abstract over hashmap vs treemap vs ... maintain invariants (e.g. that the tree is in order) Rational numbers Floating point vs fixed point vs ratio

  14. Existential Objects counterObject = {*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} = counterObject in body.methods.get(body.state) evaluates to?

  15. functions using counters (as existential objects) sendinc = c : Counter . let {X,Body} = c in {*X, {state = body.methods.inc(body.state). methods = body.methods}} as Counter sendinc : { X, {state:X, ...}} { X, {state:X, ...}}

  16. ADTs vs Objects usage: sendget (sendinc (counterObject)) usage: counter.get( counter.inc( counter.new)) type: { C, {state: C, methods: {get:C->Nat, ...}}} type: { C, {get: C-> Nat, ...}} keeps packaged structure uses internal representation set of available functions can be extended set of available functions unextendable limited support for binary operators full support for binary operators modern object oriented languages use a hybrid.

  17. Encoding existential types as universal types (with example) existential type: { C, {get: C->Nat, ...}} universal type: Y. ( C. {get: C->Nat, ...} -> Y) -> Y existential value: {*Nat, {get=id, ...}} universal value: Y. y:( C. {get: C->Nat, ...} -> Y). y[Nat]({get=id, ...}) existential usage: let (Counter, counter) = p in v universal usage: p[V]( Counter. counter. v)

More Related Content

giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#