1 / 33

Unifying Object-Oriented Programming with Typed Functional Programming

Unifying Object-Oriented Programming with Typed Functional Programming. Hongwei Xi Boston University. Talk Overview. Motivation for this work Some difficult issues in designing a type system to support object-oriented programming

cian
Download Presentation

Unifying Object-Oriented Programming with Typed Functional Programming

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  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. 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

  4. 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

  5. A Problem with Return Types class ObjClass {…virtual copy: ObjClass; …} class StringClass inherits ObjClass {… copy = … …}

  6. A Problem with Binary Methods class eqClass {virtual equal (other: eqClass): bool;…} class aEqClass inherits eqClass { … } class bEqClass inherits eqClass { … }

  7. A Puzzling Example class objClass {…virtual copy: objClass; …} class int1Class inherits objClass{…copy = ... ;…} class int2Class inherits int1Class {…}

  8. Types for Messages • We use MSG as a type constructor for constructing types for messages • Given a type t, the type (t)MSGis for messages that require objects to return values of type t after receiving them.

  9. Message Constructors • We can use some syntax to declare message constructors:MSGgetfst: (int) MSGMSGsetfst: int -> (unit) MSGMSGgetsnd: (int) MSGMSGsetsnd: int -> (unit) MSG

  10. A Type for Objects • We can now use the following type for objects:OBJ = {’a}. ’a MSG -> ’a

  11. Object Constructor for IntPairClass fun newIntPair (x:int, y:int): OBJ = letval xref = ref x val yref = ref yfun dispatch (MSGgetfst) = !xref | dispatch (MSGsetfst x’) = (xref := x’) | dispatch (MSGgetsnd) = !yref | dispatch (MSGsetsnd y’) = (yref := y’) | dispatch msg = raise UnknownMessage in dispatch end

  12. A Serious Problem • As in Smalltalk, there is so far no type-level differentiation between objects • 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

  13. 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)

  14. 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)

  15. Object Constructor for PairClass fun newPair (x, y) = letval xref = ref xval yref = ref yfun 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)

  16. Subclasses • We define a relation < on class tags{’a,’b}. (’a,’b)ColoredPairClass < (’a,’b)PairClass • The type of a message constructor is now of the following form:{’a1,…,’an,c <= C} t1 -> (t2) 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)

  17. 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)

  18. Covariant Use of SelfType class objClass {… …MSGclone: SelfType… … } MSGclone: {c <= ObjClass}. (OBJ(c)) MSG(c)

  19. 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 <= ObjClassMSGget1: {c <= Int1Class} (int) MSG(c)MSGset1: {c <= Int1Class} int -> (unit) MSG(c)MSGdouble: {c <= Int1Class} (unit) MSG(c)

  20. 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)

  21. Examples of Super Functions • fun superObj (self) = letfun dispatch (MSGclone) = self | dispatch msg = raise UnknownMessagein dispatch endwithtype {c <= ObjClass} OBJ(c) -> OBJ(c) • fun superInt1 (self) = let fun dispatch (MSGdouble) =self(MSGset1(2 * self(MSGget1))) | dispatch msg = superObj self msgin dispatch endwithtype {c <= Int1Class} OBJ(c) -> OBJ(c)

  22. 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 msgin dispatch endwithtype int -> OBJ(Int1Class)

  23. 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)

  24. Super Function and Constructor for Int2Class fun superInt2 (self) = letfun dispatch msg = superInt1 self msg withtype {c <= Int2Class} OBJ(c) -> OBJ(c) fun newInt2 (x1, x2) = letval x1ref = ref x1val x2ref = ref x2fun 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)

  25. 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

  26. Subtyping • Given a class tag C,OBJECT(C) = [c <= C] OBJ(c) • E.g.,OBJ((OBJECT(eqClass),OBJECT(eqClass))pairClass)is the type for pairs whose both components support equality test

  27. 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

  28. 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

  29. 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

  30. 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

  31. 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 …

  32. 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

  33. End of the Talk • Thank You! Questions?

More Related