Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using eval() in DT seems not consistent in terms of external variables #4798

Open
shrektan opened this issue Nov 4, 2020 · 2 comments
Open
Assignees
Labels
programming parameterizing queries: get, mget, eval, env

Comments

@shrektan
Copy link
Member

shrektan commented Nov 4, 2020

See the example below

fun = function(x) {
  x = substitute(x)
  dt <- data.table::data.table(A = 1:5, B = 11:15)
  envir <- parent.frame()
  print(eval(x, envir = envir))
  dt[eval(x, envir = envir)]
}

## This will FAIL ##
local({
  y = 5
  fun(y==5)
})
#> [1] TRUE
#> Error in .checkTypos(e, names_x): Object 'y' not found. Perhaps you intended A, B

## But this works fine ##
z = 5
fun(z==5)
#> [1] TRUE
#>    A  B
#> 1: 1 11
#> 2: 2 12
#> 3: 3 13
#> 4: 4 14
#> 5: 5 15

Created on 2020-11-04 by the reprex package (v0.3.0)

The error is thrown from

data.table/R/data.table.R

Lines 357 to 364 in 70b6b13

ienv = new.env(parent=parent.frame())
if (getOption("datatable.optimize")>=1L) assign("order", forder, ienv)
i = tryCatch(eval(.massagei(isub), x, ienv), error=function(e) {
if (grepl(":=.*defined for use in j.*only", e$message))
stop("Operator := detected in i, the first argument inside DT[...], but is only valid in the second argument, j. Most often, this happens when forgetting the first comma (e.g. DT[newvar := 5] instead of DT[ , new_var := 5]). Please double-check the syntax. Run traceback(), and debugger() to get a line number.")
else
.checkTypos(e, names_x)
})

@jangorecki
Copy link
Member

jangorecki commented Nov 4, 2020

eval calls inside [ are handled specially to allow easier metaprogramming.
@shrektan see

if (isub %iscall% "eval") { # TO DO: or ..()


#4304 is meant to provide an alternative interface for this kind of operations, which should be more reliable.
It takes out eval call from i. Note that arguments of env are not lazily evaluated. So this version is more like computing y==5 and then providing to i, which may not be exactly what you want.

fun = function(x) {
  x = substitute(x)
  dt <- data.table::data.table(A = 1:5, B = 11:15)
  envir <- parent.frame()
  dt[.x, verbose=TRUE,
     env = list(.x=eval(x, envir = envir))]
}

local({
  y = 5
  fun(y==5)
})
#Argument 'i'  after substitute: TRUE
#       A     B
#   <int> <int>
#1:     1    11
#2:     2    12
#3:     3    13
#4:     4    14
#5:     5    15

@jangorecki jangorecki added the programming parameterizing queries: get, mget, eval, env label Nov 4, 2020
@shrektan
Copy link
Member Author

shrektan commented Nov 11, 2020

Note that arguments of env are not lazily evaluated. So this version is more like computing y==5 and then providing to i, which may not be exactly what you want.

@jangorecki , it means the below codes (this is what I really want to do) won't work then, correct? As eval() can't find the value of A in your example provided.

local({
  y = 5
  fun(A %in% y)
})

I'll see if there's an easy and straightforward fix for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
programming parameterizing queries: get, mget, eval, env
Projects
None yet
Development

No branches or pull requests

2 participants