Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is there a difference between length(f) and length(g) in this example?

Tags:

r

f <- function() 1
g <- function() 2
class(g) <- "function"
class(f)  ## "function"
class(g)  ## "function"
length.function <- function(x) "function"
length(f)  ## 1
length(g)  ## "function"
like image 599
Bai Avatar asked Dec 23 '14 17:12

Bai


1 Answers

First, length is not a typical generic function, but rather an "Internal Generic Function". You can see this by looking at its definition:

> length
function (x)  .Primitive("length")

Compare this to a typical generic function:

> print
function (x, ...) 
UseMethod("print")
<bytecode: 0x116ca6f90>
<environment: namespace:base>

length calls straight into .Primitive which then can do dispatch if it does not handle the call itself; the typical approach is directly calling UseMethod which only handles dispatch. Also note that there is no length.default function because the code in the .Primitive call does that:

> methods("length")
[1] length.function length.pdf_doc* length.POSIXlt 

I am not sure it is completely defined when an Internal Generic will look at user defined methods and when it will use only internal ones; I think the general idea is that for a user/package defined (effectively, non-core) class, provided methods will be used. But overriding for internal classes may or may not work.

Additionally (though not strictly relevant for this case), even for a typical generic method, the documentation is ambiguous as to what should happen when the class is derived implicitly rather than given as an attribute. First, what class() reports is an amalgamation of things. From the class help page:

Many R objects have a class attribute, a character vector giving the names of the classes from which the object inherits. If the object does not have a class attribute, it has an implicit class, "matrix", "array" or the result of mode(x) (except that integer vectors have implicit class "integer").

So despite class returning the same thing for f and g, they are not the same.

> attributes(f)
$srcref
function() 1

> attributes(g)
$srcref
function() 2

$class
[1] "function"

Now, here is where it gets ambiguous. Method dispatch is talked about in (at least) 2 places: the class help page and the UseMethod help page. UseMethod says:

When a function calling UseMethod("fun") is applied to an object with class attribute c("first", "second"), the system searches for a function called fun.first and, if it finds it, applies it to the object. If no such function is found a function called fun.second is tried. If no class name produces a suitable function, the function fun.default is used, if it exists, or an error results.

While class says:

When a generic function fun is applied to an object with class attribute c("first", "second"), the system searches for a function called fun.first and, if it finds it, applies it to the object. If no such function is found, a function called fun.second is tried. If no class name produces a suitable function, the function fun.default is used (if it exists). If there is no class attribute, the implicit class is tried, then the default method.

The real difference is in the last sentence that the class page has that UseMethod doesn't. UseMethod does not say what happens if there is no class attribute; class says that the implicit class is used to dispatch. Your code seems to indicate that what is documented in class is not correct, as length.function would have been called for g were it.

What really happens in method dispatch when there is no class attribute will probably require examining the source code as the documentation does not seem to help.

like image 70
Brian Diggs Avatar answered Nov 15 '22 05:11

Brian Diggs