smingerson
smingerson

Reputation: 1438

Capture argument expression used for S3 dispatch

I came across an unexpected divergence of behavior between rlang and base. Differences clearly exist, as evidenced by this quote. This was from 0.4.1, but similar language is still in 0.4.2, now referring to enquo instead.

In terms of base functions, enexpr(arg) corresponds to base::substitute(arg) (though that function also features complex substitution semantics)

  1. Can anyone match the behavior using rlang functions that substitute() has?
  2. Is there a good explanation for why enexpr() doesn't work? I am guessing because it is related to S3 method dispatch.
  3. I am specifically interested how to do this using rlang, I already can achieve the behavior I want using just base.

My example is nonsense, but what it highlights is I am unable to capture the expression (in this case a single symbol, df) passed in as the first argument to a generic S3 function.

library(rlang)
f <- function(obj) {
  enexpr(obj)
}
print(f(print("hello")))
#> print("hello")
# behavior matches expectations

df <- data.frame(a = 1:5, b = letters[1:5])
class(df) <- c("custom", class(df))
`[.custom` <- function(x, i) {
call2(expr(`[`), enexpr(x), i)
}
df[4]
#> list(a = 1:5, b = 1:5)[4]
sloop::s3_dispatch(df[4])
#> => [.custom
#>  * [.data.frame
#>    [.default
#>  * [ (internal)
# It's dispatching as expected, but I don't get
# `df[4]` back as a call. 

df <- data.frame(a = 1:5, b = letters[1:5])
class(df) <- c("custom", class(df))
`[.custom` <- function(x, i) {
  call2(expr(`[`),substitute(x), i)
}
df[4]
#> df[4]
# substitute works

packageVersion("rlang")
#> [1] '0.4.2'
# Created on 2019-12-05 by the reprex package (v0.3.0)

Upvotes: 2

Views: 90

Answers (1)

Artem Sokolov
Artem Sokolov

Reputation: 13691

This is a documented issue for rlang, and support is likely to be added in future versions.

The current workaround is to introduce another level of quoting at dispatch:

`[` <- function(x, i) {
  ex <- eval_tidy( enquo(x) )
  UseMethod('[', ex)
}

`[.custom` <- function(x, i)
  call2(expr(`[`), enexpr(x), i)

df[4]
# df[4]

Upvotes: 1

Related Questions