Message Passing, part 2 – Object-Oriented Method Invocation
This is part two of an article exploring what we mean when we say “message-passing”. Part one described how synchronous rendezvous can be expressed with actors. Part two describes an actor implementation of object-oriented method invocation. For Object-Oriented developers from the Smalltalk tradition, message-passing involves a dynamic method lookup, invocation of that method with the target object as an implicit parameter, and return of a result object. We will deconstruct this mechanism and show it in more primitive form, using actors.
Ian Piumarta and his colleagues on the FoNC project propose a primitive “pervasively late-bound” object model [1]. This model defines a small collection of basic objects and methods, centering around dynamic method lookup and invocation. Late-binding of the lookup mechanism is used to implement an impressive variety of object-oriented method invocation mechanisms. These include single inheritance, multiple inheritance, and sideways composition (Lieberman prototypes) [2]. Unfortunately, even this primitive object-oriented method invocation mechanism itself “binds” certain design decisions, such as the call-lookup-invoke-return protocol. Synchronous rendezvous and asynchronous messaging are not supported by this mechanism. Instead, we will use actor primitives to express object-oriented method invocation as a pattern of asynchronous messages.
Implementing Object-Oriented Method Invocation
Object-oriented method invocation involves method lookup, method invocation with an implicit “self” parameter, and return of a result object. Once the applicable method is found, it is invoked in the context of the target object (self). Eventually a result object is returned to the original caller. The FoNC project gains considerable flexibility by allowing late-binding of method-lookup strategies. For example, a simple chain of method dictionaries could provide appropriate semantics for single-inheritance. We encapsulate the lookup policy in the “class” actor, allowing flexibility in selecting the strategy. The “class” is provided as part of the “object” state. The “object” behavior supports access to instance variables and invocation of methods.
LET object_beh(class, vars) = \(cust, req).[ CASE req OF (#get, var) : [ SEND (cust, SELF, #get, var) TO vars ] (#set, var, value) : [ SEND (cust, SELF, #set, var, value) TO vars ] (#call, selector, args) : [ CREATE call WITH call_beh(cust, SELF, selector, args) SEND (call, #lookup, selector, args) TO class ] END ]
When an object receives a #get
or #set
request, the request is delegated to the instance variables along with the identity of the original object “self”. Instance variable are described in more detail later.
When an object receives a method invocation request, it first must perform a lookup to find the applicable method to invoke. This is done by sending a #lookup
request to the “class” object. The #lookup
request includes the selector and the arguments. The class object is free to use any means it wants to find the applicable method. It may use only the selector, or it may use some or all of the arguments.
The customer of the #lookup
request is a dynamically created “call” actor. The “call” actor remembers the original caller, the instance on which to apply the method, the selector and the arguments.
LET call_beh(caller, instance, selector, args) = \method.[ SEND (caller, instance, selector, args) TO method ]
The struct_class implements structured records with public data fields. This simple “class” implementation determines the applicable method by matching a static set of selectors. The only methods it understands are #get
and #set
.
CREATE struct_class WITH \(call, #lookup, selector, args).[ CASE selector OF #get : [ SEND get_method TO call ] #set : [ SEND set_method TO call ] END ]
When a method is returned from a lookup, the method is “invoked” by sending the original caller, the “self” instance, the selector and the arguments to the “method” actor. The method may read and write instance variables by sending #get
and #set
messages to the “instance” actor. The get_method and set_method returned by struct_class use these #get
and #set
messages.
CREATE get_method WITH \(caller, instance, selector, args).[ SEND (caller, #get, args) TO instance ] CREATE set_method WITH \(caller, instance, selector, args).[ SEND (caller, #set, args) TO instance ]
Public access to fields can be implemented with generic methods such as the get_method and set_method shown here. A #get
message retrieves the field value and sends it to the original caller. A #set
message updates the field with a new value and sends the instance to the original caller. Note that these method-actors can be reused in many classes and applied to any object.
Each variable is represented by a separate actor. The state of a variable actor includes its name, its value, and a link to the next variable.
LET variable_beh(name, value, next) = \(cust, instance, req).[ CASE req OF (#get, $name) : [ SEND value TO cust ] (#set, $name, value') : [ BECOME variable_beh(name, value', next) SEND instance TO cust ] _ : [ SEND (cust, instance, req) TO next ] END ]
When a variable receives a #get
request with a matching name, the value is sent to the customer. When a variable receives a #set
request with a matching name, the actor retains the new value and sends the “self” object instance to the customer. All other requests are forwarded to the next variable in the chain. The chain ends with a special “undefined” actor, which signals an error.
CREATE y_var WITH variable_beh(#y, 1, UNDEF) CREATE x_var WITH variable_beh(#x, 0, y_var) CREATE a_point WITH object_beh(struct_class, x_var)
Given these definitions, we create an example Point object with instance variables x = 0 and y = 1, as illustrated in Figure 1.
Now we focus on the message flow involved in handling a “set” method call to set the instance variable y to -1, as illustrated in Figure 2. Note that the object instance itself is the final “result” returned.
Each part of the meta-object protocol is represented by a separate actor. This gives very fine-grained control over the process of invoking methods on objects. A shared instance of struct_class defines the method lookup policy for any simple structured record object. Shared instances of set_method and get_method provide “setter” and “getter” implementations for any objects’ instance variables.
More sophisticated class implementations could restrict access to read-only by withholding the “set” method. Access can be controlled for specific variables by matching their names as part of the lookup policy. Method calls may be serialized to prevent concurrent access to shared state. Every part of the meta-object protocol is explicitly exposed and reconfigurable, even within a running system. Mechanisms and policies may even vary between different groups of objects, if desired. Expressing the protocol of object-oriented method calls in terms of actors provides a powerful mechanism for understanding and modifying the semantics of the object model itself.
Conclusion
The phrase “message-passing” has a wide range of accepted meanings. Asynchronous messaging is the most primitive in terms of information content. In part one, we implemented synchronous rendezvous. In part two, we showed how the basic mechanism of object-oriented method invocation can be implemented on the asynchronous messaging foundation provided by the Actor model of computation. The additional information flow implicit in these models is made explicit through an actor-based implementation. This allows us to better understand (and explore modifications of) each message-passing model.
References
- [1]
- I. Piumarta. Open, extensible object models. VPRI Memo 2006-003-a, http://vpri.org/pdf/tr2006003a_objmod.pdf
- [2]
- I. Piumarta. Efficient Sideways Composition via ‘Lieberman’ Prototypes. VPRI Memo 2007-002-a, http://vpri.org/pdf/m2007002a_lieb_proto.pdf
Tags: actor, COLA, composition, FoNC, message-passing, object-oriented, protocol, Smalltalk
[…] More here: Message Passing, part 2 – Object-Oriented Method Invocation […]
[…] This post was mentioned on Twitter by Dale Schumacher, Elizabeth Carls. Elizabeth Carls said: This is what my geeky super-genius partner @dalnefre does for fun in his spare time: Object-Oriented Method Invocation http://bit.ly/9y0NkV […]