Unifying Object-Oriented Programming with Typed Functional Programming

handprintSoftware and s/w Development

Nov 18, 2013 (3 years and 4 months ago)

62 views

1

Unifying Object
-
Oriented
Programming with Typed
Functional Programming

Hongwei Xi

Boston University

2

Talk Overview


Motivation for this work


Some difficult issues in designing a type
system to support object
-
oriented
programming


An approach to implementing objects
through the use of guarded recursive
datatype constructors

3

An Example in Scheme

(define (newpair x y)


(define (dispatch msg)


(cond ((eq? msg ‘getfst) x)


((eq? msg ‘getsnd) y)


((eq? msg ‘setfst)


(lambda (x1) (set! x x1)))


((eq? msg ‘setsnd)


(lambda (y1) (set! y y1)))))

dispatch)

4

Motivation


Support object
-
oriented programming in
a typed functional programming
language


Take a CLOS
-
like approach to object
-
oriented programming


Following Smalltalk, objects are not treated
as records in this approach

5

Difficulties in Typing Objects


All existing approaches to typing objects that
we know contain some major deficiencies


The programmer needs to program around the
deficiencies


Run
-
time type checks are employed to overcome
the deficiencies


Some deficiencies exist even in a bare
minimum core that supports object
-
oriented
programming

6

A Problem with Return Types

class ObjClass {



virtual copy: ObjClass;




}


class StringClass inherits ObjClass {




copy = …




}

7

A Problem with Binary Methods

class eqClass {

virtual equal (other: eqClass): bool;



}


class aEqClass inherits eqClass { … }

class bEqClass inherits eqClass { … }

8

A Puzzling Example

class objClass {



virtual copy: objClass;




}


class int1Class inherits objClass{



copy = ... ;



}


class int2Class inherits int1Class {




}

9

Types for Messages


We use
MSG

as a type constructor for
constructing types for messages


Given a type
t
, the type
(t)
MSG

is for
messages that require objects to return
values of type
t

after receiving them.

10

Message Constructors


We can use some syntax to declare
message constructors:


MSGgetfst: (int) MSG

MSGsetfst: int
-
> (unit) MSG

MSGgetsnd: (int) MSG

MSGsetsnd: int
-
> (unit) MSG


11

A Type for Objects


We can now use the following type for
objects:



OBJ = {’a}. ’a MSG
-
> ’a


(We write
{’a}

for

a
)

12

Object Constructor for IntPairClass

fun

newIntPair (x:int, y:int): OBJ =
let


val

xref = ref x


val
yref = ref y


fun

dispatch (MSGgetfst) = !xref


| dispatch (MSGsetfst x’) = (xref := x’)


| dispatch (MSGgetsnd) = !yref


| dispatch (MSGsetsnd y’) = (yref := y’)


| dispatch msg = raise UnknownMessage

in
dispatch
end

13

A Serious Problem


As in Smalltalk, we so far have no
differentiation between objects at type
-
level


For instance, let
anIntPair

be an object
constructed by calling
newIntPair
. If we
send a message
MSGfoo
to
anIntPair
, then
an exception is to be thrown at run
-
time.


We would really like to have a type system
that can stop this at compile
-
time

14

Class Tags


We treat classes as tags.


A message type is now of the form:
(t)
MSG(C)
, where
C

is a class tag.


For instance,

MSGgetfst: (int)MSG(IntPairClass)

MSGsetfst: int
-
> (unit)MSG(IntPairClass)


The type of an object is of the form:


OBJ(C) = {’a}. ’a MSG(C)
-
> ’a


For instance,

newIntPair: int
*

int
-
>

OBJ(IntPairClass)



15

Parameterized Class Tags


There is an immediate need for class
tags parameterized over types:


MSGgetfst: {’a,’b}. (’a) MSG((’a,’b)PairClass)

MSGsetfst: {’a,’b}. ’a
-
> (unit) MSG((’a,’b)PairClass)
MSGgetsnd: {’a,’b}. (’b) MSG((’a,’b)PairClass)

MSGsetsnd: {’a,’b}. ’b
-
> (unit) MSG((’a,’b)PairClass)

16

Object Constructor for PairClass

fun
newPair (x, y) =
let

val

xref = ref x

val
yref = ref y

fun

dispatch (MSGgetfst) = !xref


| dispatch (MSGsetfst x’) = (xref := x’)


| dispatch (MSGgetsnd) = !yref


| dispatch (MSGsetsnd y’) = (yref := y’)

in
dispatch
end

withtype

{’a,’b}. ’a
*

’b
-
> OBJ((’a,’b)PairClass)

17

Subclasses


We define a relation
<=
=
潮⁣污獳s瑡杳
=
{’a,’b}. (’a,’b)ColoredPairClass <= (’a,’b)PairClass


The type of a message constructor is
now of the following form:

{’a
1
,…,’a
n
,c <= C}
t
1

-
> (
t
2
) MSG(c)


For instance,

MSGgetfst: {’a,’b,c<= (’a,’b)PairClass}. (’a) MSG(c)

MSGsetfst: {’a,’b,c<= (’a,’b)PairClass}. ’a
-
> (unit) MSG(c)

18

Contravariant Use of SelfType

class

EqClass {

MSGeq:
SelfType
-
> bool;

MSGneq:
SelfType
-
> bool;

}


MSGeq: {c <= EqClass}. OBJ(c)
-
> (bool) MSG(c)

MSGneq: {c <= EqClass}. OBJ(c)
-
> (bool) MSG(c)

19

Covariant Use of SelfType

class
objClass {

… …

MSGclone:
SelfType

… …

}


MSGclone: {c <= ObjClass}. (OBJ(c)) MSG(c)

20

Inheritance


We explain how inheritance can be implemented.


class

Int1Class
inherits

ObjClass {


MSGget1: int;


MSGset1: int
-
> unit;


MSGdouble: unit =>
self
(MSGset1(2

*

self
(MSGget1)));

}



The declaration essentially does the following:

Int1Class <= ObjClass

MSGget1: {c <= Int1Class} (int) MSG(c)

MSGset1: {c <= Int1Class} int
-
> (unit) MSG(c)

MSGdouble: {c <= Int1Class} (unit) MSG(c)


21

Super Functions


Each class is associated with a “super”
function


Given a class C, the super function
associated with C has the type:



{c <= C}. OBJ(c)
-
> OBJ(c)

22

Examples of Super Functions


fun

superObj (self) =
let


fun

dispatch (MSGclone) = self


| dispatch msg = raise UnknownMessage

in
dispatch
end

withtype

{c <= ObjClass} OBJ(c)
-
> OBJ(c)



fun

superInt1 (self) =
let



fun

dispatch (MSGdouble) =


self(MSGset1(2
*

self(MSGget1)))


| dispatch msg = superObj self msg

in

dispatch
end

withtype

{c <= Int1Class} OBJ(c)
-
> OBJ(c)


23

Object Constructor for Int1Class


We implement an object constructor for
the Int1Class as follows:


fun

newInt1 (x)=
let



val
xref = ref x



fun
dispatch (MSGget1) = !xref


| dispatch (MSGset1 x’) = (xref := x’)


| dispatch (MSGclone) = newInt1 (!xref)


| dispatch msg = superInt1 dispatch msg

in
dispatch
end

withtype

int
-
> OBJ(Int1Class)

24

A Class Declaration

class

Int2Class
inherits

Int1Class {

MSGget2: int;

MSGset2: int
-
> unit;

}

The declaration essentially does the following:

Int2Class <= Int1Class

MSGget2: {c <= Int2Class} (int) MSG(c)

MSGset2: {c <= Int2Class} int
-
> (unit) MSG(c)


25

Super Function and Constructor for Int2Class

fun

superInt2 (self) =
let

fun

dispatch msg = superInt1 self msg

withtype

{c <= Int2Class} OBJ(c)
-
> OBJ(c)


fun

newInt2 (x1, x2) =
let

val

x1ref = ref x1

val
x2ref = ref x2

fun
dispatch (MSGget1) = !x1ref


| dispatch (MSGset1 x) = (x1ref := x)


| dispatch (MSGget2) = !x2ref


| dispatch (MSGset2 x) = (x2ref := x)


| dispatch msg = superInt2 dispatch msg

in

dispatch
end

withtype

int * int
-
> OBJ(Int2Class)

26

Some Interesting Questions


Let
O2

be an object created by calling
newInt2


What happens if we send the message
MSGdouble
to
O2
:
O2(MSGdouble)
?


No method is implemented for
MSGdouble

in
newInt2


No method is implemented for
MSGdouble

in
superInt2


A method lookup finds it through

superInt1


What happens if we sent the message
MSGclone

to
O2
:
O2(MSGclone)
?


No method is implemented for
MSGclone

in
newInt2


No method is implemented for
MSGclone

in
superInt2


No method is implemented for
MSGclone

in
superInt1


A method finds it through
superObj

27

Subtyping


Given a class tag C,



OBJECT(C) = [c <= C] OBJ(c)


(we use […] for the existential quantifer

)



E.g.,


OBJ((OBJECT(eqClass),OBJECT(eqClass))pairClass)


is the type for pairs whose both components
support equality test

28

Type Representation

typecon

(type) TY =


(int) TYint

| {’a,’b}. (’a * ’b) TYtup of ’a TY * ’b TY

| {’a,’b}. (’a
-
> ’b) TYfun of ’a TY * ’b TY

| {’a}. (’a TY) TYtyp of ’a TY


TYint: (int) TY

TYtup: {’a,’b}. ’a TY * ’b TY
-
> (’a * ’b) TY

TYfun: {’a,’b}. ’a TY * ’b TY
-
> (’a
-
> ’b) TY

TYtyp: {’a}. ’a TY
-
> (’a TY) TY


29

An Example: val2string

fun
val2string pf x =

case

pf
of


TYint => int2string x

| TYtup (pf1, pf2) =>


“(” ^ val2string pf1 (fst x) ^ “,”


^ val2string pf2 (snd x) ^ “)”

| TYfun _ => “[a function]”

| TYtyp _ => “[a type]”

withtype

{’a}. ’a TY
-
> ’a
-
> string

30

End of the Talk


Thank You!

Questions
?

31

H.O.A.S. Trees

typecon

(type) HOAS =


{’a}. (’a) HOASlift of ’a

| {’a}. (’a) HOASif of bool HOAS * ’a HOAS * ’a HOAS

| {’a,’b}. (’a * ’b) HOAStup of ’a HOAS * ’b HOAS

| {’a,’b}. (’a
-
> ’b) HOASlam of ’a HOAS
-
> ’b HOAS

| {’a}. (’a) HOASfix of ’a HOAS
-
> ’a HOAS


HOASlift: {’a}. ’a
-
> ’a HOAS

HOASif: {’a}. bool HOAS * ’a HOAS * ’a HOAS
-
> ’a HOAS

HOAStup: {’a,’b}. ’a HOAS * ’b HOAS
-
> (’a * ’b) HOAS

HOASlam: {’a,’b}. (’a HOAS
-
> ’b HOAS)
-
> (’a
-
> ’b) HOAS

HOASfix: {’a}. (’a HOAS
-
> ’a HOAS)
-
> ’a HOAS

32

Type
-
Preserving Evaluation

fun
eval (HOASlift v) = v

| eval (HOASif (b, e1, e2)) =


if eval (b) then eval (e1) else eval (e2)

| eval (HOAStup (e1, e2)) =


(eval (e1), eval (e2))

| eval (HOASlam (f)) =


fn x => eval (f (HOASlift x))

| eval (HOASfix f) = eval (f (HOASfix f))

withtype

{’a}. ’a HOAS
-
> ’a

33

F.O.A.S. trees

typecon

(type,type) FOAS =

| {’a,’g}. (’a,’g) FOASlift of ’a

| {’a,’g}. (’a,’a*’g) FOASone

| {’a,’b,’g}. (’a,’b*’g) FOASshift of (’a,’g) FOAS

| {’a,’b,’g}. (’a
-
> ’b,’g) FOASlam of (’b,’a*’g) FOAS

| …


FOASlift: {’a,’g}. ’a
-
> (’a,’g) FOAS

FOASone: {’a,’g}. (’a,’a*’g) FOAS

FOASshift: {’a, ’b,’g}. (’a,’g) FOAS
-
> (’a,’b*’g) FOAS

FOASlam: {’a,’b,’g}. (’b,’a * ’g) FOAS
-
> (’a
-
> ’b,’g) FOAS




34

Type
-
Preserving Evaluation

fun
eval (FOASlift v) env = v

| eval FOASone (v, env) = v

| eval (FOASshift e) (_, env) = eval e env

| eval (FOASlam e) env = fn x => eval e (x, env)

| …

withtype

{’a}. (’a,’g) FOAS
-
> ’g
-
> ’a


fun

evaluate (e) = eval e ()

withtype

{’a}. (’a,unit) FOAS
-
> ’a