捕获用于 S3 调度的参数表达式

Capture argument expression used for S3 dispatch

我在 rlang 和 base 之间发现了意想不到的行为差异。正如这句话所证明的那样,差异显然存在。这是从 0.4.1 开始的,但类似的语言仍在 0.4.2 中,现在指的是 enquo

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

  1. 任何人都可以使用 substitute() 具有的 rlang 函数匹配行为吗?
  2. 对于为什么 enexpr() 不起作用有一个很好的解释吗?我猜是因为它与 S3 方法调度有关。
  3. 我对如何使用 rlang 执行此操作特别感兴趣,我已经可以仅使用 base 实现我想要的行为。

我的示例是无稽之谈,但它强调的是我无法捕获作为第一个参数传递给通用 S3 函数的表达式(在本例中为单个符号,df)。

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)

这是 rlangdocumented issue,可能会在未来的版本中添加支持。

当前的解决方法是在调度时引入另一个级别的报价:

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

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

df[4]
# df[4]