[Rd] default arguments in generics and methods
John Chambers
jmc at r-project.org
Sat Oct 21 16:28:36 CEST 2006
You're conflating 3 different things here: how methods are selected,
how R treats missing arguments, and how the standardGeneric() call is
implemented.
Maybe more relevant, if this question is motivated by a practical need,
you may want to rethink what the generic function does. "Nonstandard"
generic functions are called that for a reason. The concept behind
methods in S is that a generic function defines the _function_ or
purpose of the computation, but it's only the methods that define how
the purpose is carried out.
Using a nonstandard generic that does some extra computation always
blurs the concept, and often makes methods harder to write or
understand. That you've found it necessary to worry about the subtle
points in your question is a warning sign. It's usually worth asking
whether the place that you call standardGeneric() could in fact _be_ the
generic, with the other computations moved to a nongeneric wrapper
function. Having said that, when the computations depend on missing
arguments, the situation may be more complicated.
Anyway, the following relate to your question.
1. In method selection, the "missing" class is used if the C-level
equivalent of the test missing(ARG) is TRUE when selection takes place.
On to point 2.
2. The R evaluator stores a special object corresponding to missing
arguments. Missing arguments are treated by the lazy evaluation rules,
so the exact contents of the current object of this name in the local
environment will depend on whether there has been a reference to the
argument. The missing(ARG) test remains TRUE when the default
expression is evaluated but becomes FALSE if a direct assignment of ARG
takes place (just reporting this, not defending it).
3. standardGeneric() creates a new context into which it copies
bindings for all the objects in the frame of the generic function call,
but tries to maintain defaults for missing arguments. If you want to
see how it works, look at the routine R_execMethod in file
src/main/eval.c of the R source.
The somewhat unfortunate result of all this can be seen by comparing two
nonstandard generics:
setGeneric("foo", function(x, y){if(missing(y)) y <- x+1;
standardGeneric("foo")})
setGeneric("foo2", function(x, y = x+1){if(missing(y)) message(y);
standardGeneric("foo2")})
The "foo" function will never dispatch a method for c("numeric",
"missing") because y is assigned directly. The "foo2" function will
dispatch such a method though, even though the numerical value of y will
be the same in message(y).
As may be obvious, the computation is subtle and it might be foolish to
rely on the details as if they were part of the API.
Parlamis Franklin wrote:
> i believe the following is true but would appreciate confirmation
> that it is intended behavior and will continue:
>
> if a default argument is employed in the definition of a generic
> function, and the generic is called with the argument in question
> (call it 'ARG') missing, then the method for signature (..., ARG =
> "missing", ...) will be called by 'standardGeneric' whether or not
> ARG is evaluated in the body of the generic prior to the call to
> 'standardGeneric'; however, the value assigned to ARG in the method
> call will depend on whether ARG was evaluated in the body of the
> generic prior to the call to 'standardGeneric' (with the default
> value assigned in the case of prior evaluation and no value assigned
> in the case of no prior evaluation). furthermore, if ARG is
> explicitly assigned to in the body of the generic prior to the call
> to 'standardGeneric', then the method for signature (..., ARG = class
> (ARG), ...) will be called, with the assigned value of ARG passed to
> the call.
>
> this behavior is exhibited by the code below:
>
> __
>
> setGeneric("foo", function(x = 1) standardGeneric("foo"),
> useAsDefault = FALSE)
> setMethod("foo", signature(x = "missing"), function(x = 3) x)
>
> setGeneric("foo2", function(x = 1) {x; standardGeneric("foo2")},
> useAsDefault = FALSE)
> setMethod("foo2", signature(x = "missing"), function(x = 3) x)
>
> setGeneric("foo3", function(x = 1) {x<-2; standardGeneric("foo3")},
> useAsDefault = FALSE)
> setMethod("foo3", signature(x = "missing"), function(x = 3) x)
> setMethod("foo3", signature(x = "numeric"), function(x = 3) x^2)
>
> foo() # returns 3
> foo2() # returns 1
> foo3() # returns 4
>
> ___
>
> franklin parlamis
>
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>
>
More information about the R-devel
mailing list