`[.factor` raises NextMethod error when qualified by base::
Asked Answered
R

1

6

Please explain why the following errors do/don't occur:

ff = factor(1:3)
`[.factor`(ff) # okay
base::`[.factor`(ff)  # Error in NextMethod("[") : 'NextMethod' called from an anonymous function
f = `[.factor`; f(ff) # Error in NextMethod("[") : wrong value for .Method
Rok answered 5/2 at 19:22 Comment(0)
F
6

This is a surprisingly subtle and complex question.

The reason why base::`[.factor`(ff) doesn't work and `[.factor`(ff)` does even though both pieces of code literally invoke the same function is because of the unusual way in NextMethod works. The TL;DR is that `[.factor` is s symbol, but base::`[.factor` is a call.

When you invoke NextMethod, the underlying C code works out what context it was called in, and will throw an error if any of several conditions aren't met.

One of these conditions is that the function in which NextMethod resides has to have been called directly - in technical terms, the first node in the call has to be a symbol, not an expression. If you try to get the function via a call, NextMethod will detect that and give you the "anonymous function" error.

NextMethod also checks that the name of the function is consistent with the generic being used, and if there is a mismatch, we get the "wrong value for .Method" error

We can demonstrate identical behaviour with a simple example. We have an object of class "foo":

foo <- structure(1:3, class = c("foo"))

foo
#> [1] 1 2 3
#> attr(,"class")
#> [1] "foo"

We want to be able to subset a "foo" so that any subset of a "foo" is still of class "foo", but otherwise we want the exact same subsetting rules as a numeric vector. This is where NextMethod comes in:

`[.foo` <- function(obj, ind) {
  y <- NextMethod("[")
  class(y) <- "foo"
  y
}


foo[2]
#> [1] 2
#> attr(,"class")
#> [1] "foo"

This works as expected, and so does calling it directly:

`[.foo`(foo, 2)
#> [1] 2

But, if we try to call the function indirectly, say via get, then NextMethod throws the error because get("[.foo") is a call, not a symbol.

get("[.foo")(foo, 2)
#> Error in NextMethod("[") : 'NextMethod' called from an anonymous function

Even wrapping the call in parentheses or curly brackets is enough to generate the error (because these brackets are actually calls too)

(`[.foo`)(foo, 2)
#> Error in NextMethod("[") : 'NextMethod' called from an anonymous function

{`[.foo`}(foo, 2)
#> Error in NextMethod("[") : 'NextMethod' called from an anonymous function

If we try to rename the function, we get the same error you did with trying to rename [.factor:

f <- `[.foo`

f(foo, 2)
#> Error in NextMethod("[") : wrong value for .Method

Now f is at least a symbol, but it is the wrong symbol (f is not [)

The final piece of the puzzle is why base::`[.factor` isn't a symbol. It's because in R the namespace symbol :: is actually a call, so base::`[.factor` is actually parsed as `::`(base, `[.factor`)

class(quote(base::`[.factor`))
#> [1] "call"

Whereas, without the namespace identifier, what we have is a symbol (a.k.a. "name")

class(quote(`[.factor`))
#> [1] "name"
Feebleminded answered 5/2 at 22:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.