[Next] [Previous] [Up] [Top] [Contents] [Index]

2 Dynamically-Typed Core

2.8 Resends

Most existing object-oriented languages allow one method to override another method while preserving the ability of the overriding method to invoke the overridden version: Smalltalk-80 has super, CLOS has call-next-method, C++ has qualified messages using the :: operator, Trellis has qualified messages using the ' operator, and Self has undirected and directed resend (integrating unqualified super-like messages and qualified messages). Such a facility allows a method to be defined as an incremental extension of an existing method by overriding it with a new definition and invoking the overridden method as part of the implementation of the overriding method. This same facility also allows ambiguities in message lookup to be resolved by explicitly forwarding the message to a particular ancestor.

Cecil includes a construct for resending messages that adapts the Self undirected and directed resend model to the multiply-dispatched case. The syntax for a resend is as follows:

resend	::=	"resend" [ "(" resend_args ")" ]
resend_args	::=	resend_arg { "," resend_arg }
resend_arg	::=	expr	corresponding formal of sender must be
			     unspecialized
	|	name	undirected resend (name is a specialized formal)
	|	name "@" named_object	directed resend (name is a specialized formal)

The purpose of the resend construct is to allow a method to invoke one of the methods that the resending method overrides. Consequently, only methods with the same name and number of arguments as the resending method whose argument specializers are ancestors of the resending method's argument specializers are considered possible targets of a resend.

To invoke an overridden method, the normal prefix message sending syntax is used but with the following changes and restrictions:

As a syntactic convenience, if all formals of the sender are passed through as arguments to the resend unchanged, then the simple resend keyword without an argument list is sufficient.

The semantics of a resent message are similar to a normal message, except that only methods that are less specific than the resending method in the partial order over methods are considered possible matches; this has the effect of "searching upwards" in the inheritance graph to find the single most-specific method that the resending method overrides. The restrictions on the name, on the number of arguments, and on passing specialized objects through unchanged ensures that the methods considered as candidates are applicable to the name and arguments of the send. Single-dispatching languages often have similar restrictions: Smalltalk-80 requires that the implicit self argument be passed through unchanged with the super send, and CLOS's call-next-method uses the same name and arguments as the calling method.

For example, the following illustrates how resends may be used to provide incremental extensions to existing methods:

object colored_rectangle isa rectangle;
	field color(@colored_rectangle);
	method display(r@colored_rectangle, d@output_device) {
		d.color := r.color; -- set the right color for this rectangle
		resend; -- do the normal rectangle drawing; sugar for resend(r, d)
	}

Resends may also be used to explicitly resolve ambiguities in the method lookup by filtering out undesired methods. Any of the required arguments to a resend (those that are specialized formals of the resending method) may be suffixed with the @ symbol and the name of an ancestor of the corresponding argument specializer. This restricts methods considered in the resulting partial order to be those whose corresponding argument specializers (if present) are equal to or ancestors of the object named as part of the resend.

To illustrate, the following method resolves the ambiguity of height for vlsi_cell in favor of the rectangle version of height:[10]

object rectangle;
	field height(@rectangle);

object tree_node;
	method height(t@tree_node) { 1 + height(t.parent) }

object vlsi_cell isa rectangle, tree_node;
	method height(v@vlsi_cell) { resend(v@rectangle) }

This model of undirected and directed resends is a simplification of the Self rules, extended to the multiple dispatching case. Self's rules additionally support prioritized multiple inheritance and dynamic inheritance, neither of which is present in Cecil. Self also allows the name and number of arguments to be changed as part of the resend. In some cases, it appears to be useful to be able to change the name of the message as part of the resend. For example, it might be useful to be able to provide access to the tree_node version of the height method under some other name, but this currently is not possible in Cecil. We are investigating possible semantics for resends where the name of the message is changed, so that both ambiguously-inherited methods can be invoked.

As demonstrated by Self, supporting both undirected and directed resends is preferable to just supporting directed resends as does C++ and Trellis, since the resending code does not need to be changed if the local inheritance graph is adjusted. Since CLOS does not admit the possibility of ambiguity, it need only support undirected resends (i.e., call-next-method); there is no need for directed resends.


[10] This example was adapted from Ungar and Smith's original Self paper [Ungar & Smith 87].