1

I've asked a related question before, but I think this frames it in a more interesting way.

If a base R function is generic, and has one or more arguments that use "standard" nonstandard evaluation, would it be feasible, and if so, would it be sensible, to offer a "tidy" method that would make tidy evaluation techniques available for those arguments? My image of this is of some conceptually standard wrapper that could be tailored to each individual function with metaprogramming techniques. It would apply only to evaluation of arguments which currently have nonstandard evaluation, and maybe only to those that have nonstandard evaluation of the quoting type, so that all the current methods would remain available after argument matching and construction via NextMethod(), or whatever the equivalent is for S4, S6, etc.

My reason for asking is that I have been slowly and painfully cramming a gradually increasing understanding of tidy eval into my head, and it would make me happy if I thought these powerful methods would ultimately become standard for non-standard evaluation, as opposed to maintaining multiple queues of quirky nonstandard evaluation methods forever.

Of course, I'd still have to remember that different nonstandard evaluation methods would be used by primitives that are not themselves methods for some generic. (I think I am right in saying that, although generic functions can have primitive methods, primitive functions can not be, or at least are not, generic. Yes?) I'm resigned to that.

andrewH
  • 2,281
  • 2
  • 22
  • 32

1 Answers1

0

I did a few tests to try to figure out an answer, and I think it would be very inconsistent.

First of all, if you read the documentation for InternalMethods, you'll see the following:

The following primitive and internal functions are generic, i.e., you can write methods for them

So you can have primitive functions that are generic.

Here are some of my tests, although I doubt they are exhaustive.

library(rlang)

setClass("Foo", list(x="numeric"))
foo <- new("Foo", x=0)

First I tried to define a $ method, which already uses quotation for lists and data frames:

setMethod("$", signature(x="Foo"), function(x, name) {
  enquo(name)
})

foo$x
<quosure>
expr: ^"x"
env:  empty

For some reason, the generic we defined automatically changes name to character, but maybe you could simply use sym and then continue.

Then I wanted to see what happens with generics that contain the ellipsis:

setMethod("predict", signature(object="Foo"), function(object, ...) {
  enquos(...)
})

predict(foo, bar, baz=bak)
<list_of<quosure>>

[[1]]
<quosure>
expr: ^bar
env:  global

$baz
<quosure>
expr: ^bak
env:  global

No surprises there(?)

Then I tried to define [, which has several formal arguments and ...:

setMethod("[", signature(x="Foo"), function(x, i, j, ..., drop=TRUE) {
  enquos(i, j, ...)
})

foo[bar, , baz, bak]
<list_of<quosure>>

[[1]]
<quosure>
expr: ^bar
env:  global

[[2]]
<quosure>
expr: ^
env:  empty

[[3]]
<quosure>
expr: ^baz
env:  global

[[4]]
<quosure>
expr: ^bak
env:  global

Seems to work as expected, although I'm not sure what to make of the empty quosure, it doesn't really behave like a missing argument:

f <- function(x) {
  print(missing(x))
  ff <- function(xx) { missing(xx) }
  eval_tidy(ff(!!enquo(x)))
}

f()
[1] TRUE
[1] FALSE

Finally I tried adding a formal argument to the [ method:

setMethod("[", signature(x="Foo"), function(x, i, j, k, ..., drop=TRUE) {
  enquos(i, j, k, ...)
})

foo[bar, , baz, bak]
<list_of<quosure>>

[[1]]
<quosure>
expr: ^i
env:  000001FCA36865F0

[[2]]
<quosure>
expr: ^j
env:  000001FCA36865F0

[[3]]
<quosure>
expr: ^baz
env:  global

[[4]]
<quosure>
expr: ^bak
env:  global

And I don't know if that behavior is a bug or a feature or something else.

Alexis
  • 4,950
  • 1
  • 18
  • 37