Lazy Evaluation and Missing Arguments

The S evaluation model for function calls is unusual in that arguments are evaluated during evaluation of the body of the called function, not before the call, and then only when the value is really needed. Argument evaluation is described on pages 181-183 of "Programming with Data"; in particular, page 182 describes when the actual evaluation takes place; roughly, when using an interface to C, etc., when copying as for a local assignment, and when looking for a method. For the first two cases, a missing with no default produces an error then, but not before. For method search, class "missing" is legitimate in a signature, and not an error.

It might not be obvious, to say the least, what are all the consequences of lazy evaluation. There are some examples in the book that illustrate useful cases of allowing missing arguments. Also, other examples can arise in which there are two possible expressions, supplied as two arguments, but with the understanding that the called function will choose only one of these to evaluate.

As yet another variant, consider the following fragment. It's useless as is, but meant to illustrate another possible technique with lazy evaluation. Function b passes its argument to function a, but doesn't want to call a with no arguments if its own argument is missing. Instead it computes a flag to warn a not to use the argument.

> b = function(x) a(x, missing(x))
> a = function(y, useDefault) if(missing(y) || useDefault) 0 else x
> b(1)
[1] 1
> b()
[1] 0
Obviously, this is trivial as it stands, but embedded into a more elaborate context it might be a convenient simplification of the design of b.

One natural confusion might be to think that missing(y) is TRUE in the body of a. But this is not what's happening: the argument to a is never missing in the call from b. What the object passed to a actually is, on the other hand, we should never know or care.

A subtle implementation detail is that the S evaluator has to handle such situations by checking the actual object reference (to use Java terminology) rather than the contents of the object. Generally, one can't say in a language like S that any actual object is "not an object"; you get into some impossible circularity when you try to apply that idea in general.

In the example above, doing anything much other than missing(y) to the argument is going to generate an error (effectively preventing you from peeking at the actual object passed down). The reason is not a problem in a, but rather that evaluating the argument there requires evaluating the argument in b, which is missing with no default. Hence the error message below.

> b = function(x)a(x)
> a = function(y)class(y)
> b(pi)
[1] "numeric"
> b()
Problem in b(): argument "x" is a missing with no default 

John Chambers<>
Last modified: Wed Jul 29 11:25:18 EDT 1998