210 likes | 225 Views
This paper explores a typeful approach to object-oriented programming with multiple inheritance, providing a sound typing system and modeling the basic features of OOP. The paper also introduces wrapper functions and super functions to implement multiple inheritance.
E N D
A Typeful Approach to Object-Oriented Programming with Multiple Inheritance Chiyan Chen, Rui Shi, Hongwei Xi Computer Science Dept. Boston University PADL04
Motivation • Through translating high-level OOP syntax to a typed -calculus, the soundness of the typing rules for the OOP features follows from the soundness of the target system. • Within the Applied Type System (ATS) framework, we want to support OOP as an additional package without complicating the existing system. PADL04
Objects as Functions • Examples: Smalltalk • Interpretation: An object is represented as a message interpreter, that is, a function which takes messages dispatched to it as arguments and then selects the appropriate behavior according to the messages. • Message passing: function application. PADL04
Constructing an Integer Pair Object • fun newIntPair x y = let val xref = ref x and yref = ref y fun dispatch msg = case msg of | MSGgetfst => !xref | MSGgetsnd => !yref | MSGsetfst x' => (xref := x') | MSGsetsnd y' => (yref := y') | _ => raise UnknownMessage in dispatch end withtype int -> int -> OBJ(ip) • Given a class C, OBJ(C) is a shorthand for the following type: :type.MSG(C,) → PADL04
Typed OOP Model • An improved typed OOP model based on the previous work (POPL’03, Xi, Chen and Chen) • Takes the “objects as functions” view • Models the basic features: message passing, single inheritance. • Provides a natural solution to the notion of “self type”. • Subclassing is modeled with explicit proof terms. • Supports multiple inheritance PADL04
Single Inheritance vs.Multiple Inheritance • Single Inheritance: • Examples: Smalltalk, Java • Easy to reason and implement. • Multiple Inheritance: • Examples: Eiffel, C++ • Complicated model • Interaction with other OO features PADL04
Subtlety with MI in C++ A :: foo() = 0 A B1 :: foo() = 1 B1 :: bar1() = foo() B2 :: foo() = 2 B2 :: bar2() = foo() B1 B2 C :: foo() = 3 C C* pc = new C(); pc->bar1() returns 3 PADL04
Subtlety with MI in C++ A :: foo() = 0 A B1 :: foo() = 1 B1 :: bar1() = foo() B2 :: foo() = 2 B2 :: bar2() = foo() B1 B2 foo() is not overridden. C C* pc = new C(); pc->bar1() returns 1 PADL04
Subtlety with MI in C++ A :: foo() = 0 A B1 :: foo() = 1 B1 :: bar1() = foo() B2 :: foo() = 2 B2 :: bar2() = foo() B1 B2 C :: bar3 () = B1 :: foo() D* pd = new D(); pd->bar3() returns 1 C D :: foo() = 4 D PADL04
Subtlety with MI in C++ A :: foo() = 0 A B1 :: foo() = 1 B1 :: bar1() = foo() B2 :: foo() = 2 B2 :: bar2() = foo() B1 B2 C :: foo () = B1 :: foo() D* pd = new D(); pd->bar2() returns 1 C foo() is not overridden. D PADL04
Our Solution • Using explicit inheritance paths to direct method lookup. A :: foo() = 0 A B1 :: foo() = 1 B1 :: bar1() = A :: B1 :: foo() B2 :: foo() = 2 B2 :: bar2() = A :: B2 :: foo() B1 B2 C :: bar3 () = A :: B1 :: C :: foo() C D* pd = new D(); pd->C :: D :: bar3() returns 4 D :: foo() = 4 D PADL04
Run-Time Class Tags • datasort cls = obj | eq | ip | cip | … • datatype ClS (cls) = | CLSobj (obj) | CLSeq (eq) | CLSip (ip) | CLScip (cip) | ... eq obj Ip cip PADL04
Inheritance Paths • Paths from (super)classes to (sub)classes are treated as run-time proof terms (of subclass relation) • DeclarePTH as a guarded datatype, which takes two static class tags C1 and C2 to form a type PTH(C1, C2) for paths from C1 to C2 • E.g. Abbreviate a path from class objto cip as [CLSobj, CLSip, CLScip] PADL04
Regular/Temporary Objects • Given a static class tag C, OBJ(C) is the type for regular objects in class C, whichis the shorthand for the following type: c0:cls.:type.PTH(c0,C) → MSG(c0,C,) → • There is another form of objects that are only constructed during run-time calledtemporary objects. Given a static class tag C, OBJ0(C)stands for the type: c0:cls. :type.MSG(c0,C,) → • Note that a temporary object is not constructed by applying a regular object to a given path. PADL04
Wrapper Functions • A wrapper function for a class C is assigned the type WRP(C), where WRP(C) stands for the following type: OBJ(C) → OBJ0(C) • Intuitively, a wrapper function turns a regularobject in class C into a temporary object in class C. • A wrapper function is mainly used to “hard-wire” a method lookup strategy into a regular object. PADL04
Super Functions • For each class C, there is a super function associated with it, which plays a key role in implementing (multiple) inheritance. • Given a static class tag C0, the super function associated with C0is assigned the typeSUPER(C0), which is the shorthand for the following type: c:cls.PTH(C0,c) → WRP (c) → WRP (c) PADL04
An Example of Super Function • fun SUPEReq pth wrp obj = let fun dispatch msg = case msg of | MSGeq (obj') => not (obj pth (MSGneq (obj'))) | MSGneq (obj') => not (obj pth (MSGeq (obj'))) | _ => wrp obj msg in dispatch end withtype {c:cls} PTH (eq,c) → WRP(c) → WRP (c) PADL04
Chaining Super Functions Together • Given an object o, nullwrapper(o) constructs a temporary object, which raises the UnknownMessage exceptionfor any message received. • Given a run-time inheritance path p, path2wrapper(p) turns the path to a wrapper. e.g. path2wrapper [CLSobj, CLSip, CLScip] = SUPERcip [CLScip] (SUPERip [CLSip, CLScip] (SUPERobj [CLSobj, CLSip, CLScip] nullWrapper)) PADL04
Constructing Objects • newintPair is an object constructor function, which takes two integers to create an integer pair object. • fun newIntPair x y = let val xref = ref x and yref = ref y fun dispatch pth msg = case msg of | MSGcopy => newIntPair (!xref) (!yref) | MSGgetfst => !xref | MSGsetfst x' => (xref := x') | … | _ => path2wrapper pth dispatch msg in dispatch end withtype int -> int -> OBJ (ip) PADL04
Example for Method Lookup • Given two integer pair objects o1 and o2, let us consider No message handler for MSGneq(o2) [CLSeq, CLSip] (MSGneq(o2)) O1 path2wrapper[CLSeq, CLSip] o1 (MSGneq(o2)) No message handler for MSGneq(o2) SUPERip [CLSip] (SUPEReq [CLSeq, CLSip] nullWrapper) o1 (MSGneq(o2)) MSGeq(o2) finally be handled by SUPERip Has message handler for MSGneq(o2) (SUPEReq [CLSeq, CLSip] nullWrapper) o1 (MSGneq(o2)) [CLSeq, CLSip] (MSGeq(o2)) not O1 PADL04
Conclusion • We have developed a general approach to multiple inheritance in a typed setting • Representing inheritance paths as (typed) run-time proof-terms. • Different strategies to resolve dispatching ambiguity • Interaction between multiple inheritance and parametric polymorphism • A clear model which can be implemented efficiently (e.g. by following C++ implementation) PADL04