Variations on Dispatching a Method

Standard S Method Dispatch

The standard generic function in S has as its body, naturally,

which calls the internal C code to select a method based on the classes of the arguments. Section 4.9 discusses how this works; roughly, for each formal argument involved in the method selection, the evaluator matches the actual argument classes (or "missing" if the argument is missing) against the stored table of methods. The closest matching method is selected (maybe the default method) and each argument, coerced to the corresponding class, is assigned in the evaluation frame for the function. Finally, the body of the selected method is evaluated in this frame, and the result of this evaluation becomes the value of the call.

By definition, coercing the classes means that the actual argument, when the method is evaluated, has the class specified in the chosen method's signature, not necessarily its original class. This is essential in an object-based system, because the writer of the method needs to know that it's dealing with a known class (and extensions can involve coerce methods, so the actual argument may not look anything like the selected class). The partial exception is that virtual classes in the signature always expect the actual object to have some non-virtual class.

If you've used other object-oriented languages such as Java or C++, this may seem odd or restrictive, but it is part of the essential evaluation model for S. You don't need to be restricted by it, however, if you have an example where it really doesn't fit what you want to do. The S evaluator is available directly, so you can program a different method dispatch mechanism. These notes describe how.

Virtual Method Dispatch

In this mechanism, the method is selected but the arguments are not coerced. Instead the body is evaluated directly. It will see the actual arguments just as they are supplied by the calling function.
foo = function(x, y) {
(This relies on an extension to the selectMethod function post-dating the book, in which it uses the actual arguments to define the method selection.)

The methods for virtual dispatch must be specially designed. As mentioned before, they can't in general assume anything about the actual form (slots, for example) in the arguments. They can assume that the arguments extend the class for which the method was written. I imagine most such methods would extract some general information about the object (its class, for example), and then call the as function to coerce the argument to a known form.

Dispatch Without Inheritance

In this version, the method writer is guaranteed that the actual argument has the specified class, not merely that it extends that class. This is the appropriate form for functions that need to believe they have all the information they could need about the object (dput is an example). In this case the generic function has a form such as
dput = function(x = NULL, file = stdout())
  sig = selectMethodSignature("dput", class(x))
  if(identical(sig, class(x)))
  else eval(functionBody(selectMethod("dput", character())))

If the selected method matches the actual classes, do the standard thing; otherwise, select and evaluate the default method. If the function can have special methods for missing arguments, the body of the generic function will have to use something a little fancier than class(x), say
if(missing(x)) "missing" else class(x)

John Chambers<>
Last modified: Wed Feb 17 10:14:46 EST 1999