R object's name carrying through multiple functions

216 views Asked by At

In line with my reading of Hadley's advice on building S3 objects I am using a helper function, a constructor function, and a validator function. A simple reproducible example:

test_object <- function(x, y, z) {
    new_test_object(x, y, z)
}

new_test_object <- function(x, y, z) {
    structure(list(x = x,
                   y = y,
                   z = z,
                   x_name = deparse(substitute(x))),
              class = "test_object")
}

validate_test_object <- function(test_object) {
    # Anything goes
    test_object
}

I would like the resulting object to include a value with the original name that the item passed in had ($x_name in the above example). The deparse(substitute(...)) trick works if I call the constructor directly:

alpha = "a"
test_constructor <- new_test_object(x = alpha, y = "b", z = "c")
test_constructor$x_name
# [1] "alpha"

But not if I use the helper function:

test_helper <- test_object(x = alpha, y = "b", z = "c")
test_helper$x_name
# [1] "x"

I would like test_helper$x_name to also return [1] "alpha".

Short of doing the deparse(substitute(...)) step at the helper stage, is there any way of the constructor function (new_test_object()) accessing the 'original' name of the object x if it has come via the helper? Or to ensure that its name passes through with it as the helper function passes it to the constructor?

2

There are 2 answers

1
MrFlick On BEST ANSWER

What's really the purpose here? If you are just using one function as a wrapper to another, then there are better ways of preserving arguments. For example

test_object <- function(x, y, z) {
  call <- match.call()
  call[[1]]  <- quote(new_test_object)
  eval(call)
}

But in general relying on deparse() to get information from names of variables isn't a very reliable method. It would be better to have such pieces of information be proper parameters that you can set if you like. This makes your functions much more flexible.

test_object <- function(x, y, z, xname=deparse(substitute(x))) {
    new_test_object(x, y, z, xname=xname)
}

new_test_object <- function(x, y, z, xname=deparse(substitute(x))) {
    structure(list(x = x,
                   y = y,
                   z = z,
                   x_name = xname),
              class = "test_object")
}
0
Emmanuel-Lin On

Here is a not beautifull fix: you add ... argument to pass the name when you are calling it from another function

test_object <- function(x, y, z) {
  x_name = deparse(substitute(x))
  new_test_object(x, y, z, x_name = x_name)
}

new_test_object <- function(x, y, z, ...) {
  args <- list(...)
  if(is.null(args[["x_name"]])){
    structure(list(x = x,
                   y = y,
                   z = z,
                   x_name = deparse(substitute(x))),
              class = "test_object")
  }
  else{
    structure(list(x = x,
                   y = y,
                   z = z,
                   x_name = args[["x_name"]]),
              class = "test_object")
  }

}

And here is the result:

test_helper <- test_object(x = alpha, y = "b", z = "c")
test_helper$x_name
# [1] "alpha"