Current rlang equivalent of deparse(substitute(arg))?

I want to capture the "name" of the object passed to an argument of a function. In base R idioms, I would use deparse(substitute(arg)) to get the name:

foo <- function(x) { deparse(substitute(x)) }
obj <- "an object"
foo(obj)

Yielding

r$> foo(obj)
[1] "obj"

What is the rlang equivalent of this base R idiom? What I'm looking for is something that will work with an S3 classed object, and might be provided as model[["gam"]], for example:

library("mgcv")
df <- gamSim(1)
m_gamm <- gamm(y ~ s(x2), data = df)

foo(m_gamm$gam)
foo(m_gamm[["gam"]])
foo(m_gamm[[2L]])

which gives (using the base R idiom)

r$> foo(m_gamm$gam) 
    foo(m_gamm[["gam"]]) 
    foo(m_gamm[[2L]])
[1] "m_gamm$gam"
[1] "m_gamm[[\"gam\"]]"
[1] "m_gamm[[2L]]

I'm happy to do some string processing on these results to get at the m_gamm bit if I want to clean them up at all, but I'm wondering what the rlang equivalent would be, if one exists.

1 Like

:wave: @ucfagls!

Maybe rlang::get_expr()?

foo <- function(x) { 
  arg <- rlang::enquo(x)
  expr <- rlang::get_expr(arg)
  if (is(expr, "name")) {
    return(expr)
  } else {
    NA
  }
}
obj <- "an object"
foo(obj)
#> obj
foo(1)
#> [1] NA

Created on 2022-03-22 by the reprex package (v2.0.1)
Not sure how much it helps with your actual example, though.

1 Like

In the second example it might help to use rlang::call_args(rlang::get_expr(arg)).
Now, I'm definitely no expert and just playing around with what I see in Function reference • rlang :sweat_smile:
Curious to see what you come up with in the end!

1 Like

Thanks @maelle Your suggestions don't work for the kind of model object I am working with

Your first suggestion returns NA like it does for 1, so that point in the code get_expr isn't returning a name.

For your second example I wasn't 100% sure where you intended to insert/substitute the call_args() bit, but the attempts I tried always return

Error in `rlang::call_args()`:
! `call` must be a defused call, not a symbol.

I can get closer by doing

foo <- function(x) { 
  expr <- rlang::enquo(x)
  expr 
}

r$> foo(m_gamm$gam)
<quosure>
expr: ^m_gamm$gam
env:  global

where the quosure seems to have captured the expression I want (although it's prepended ^ for some reason that I don't understand). From there I got to

foo <- function(x) { 
  expr <- rlang::enquo(x)
  rlang::expr_text(expr) 
}

which gets me very close but now I have a prepended ~ (!)

r$> foo(m_gamm$gam)
[1] "~m_gamm$gam"

I could easily get rid of the "~" part of this string, but that seems convoluted compared to deparse(substitute(arg)) which is what I have used for the time being in my package code.

But I'm getting closer: thanks :smile:

1 Like

I meant something like

foo <- function(x) { 
  arg <- rlang::enquo(x)
  expr <- rlang::get_expr(arg)
  if (is(expr, "name")) {
    return(expr)
  } else if(is(expr, "call")) {
    rlang::call_args(expr)
  } else {
    NA
  }
}

obj2 <- list(pof = 1, bla = 2)
foo(obj2$pof)
#> [[1]]
#> obj2
#> 
#> [[2]]
#> pof

Created on 2022-03-22 by the reprex package (v2.0.1)

But not sure it's useful at all!

1 Like

Uggh, I was so close! (but also Yay!)

Having got to rlang::expr_text(rlang::enquo(arg)) I noticed that the example there mentions using substitute() in place of rlang::enquo(). So I tried that and the extra decoration that expr_text() was adding (or perhaps rlang::enquo() was what was adding if) is gone:

fn <- function(x) rlang::expr_text(substitute(x))
fn(m_gamm$gam)
r$> fn(m_gamm$gam)
[1] "m_gamm$gam"

So, while I don't understand the decoration added to expressions with enquo, I can avoid that and get what I want with substitute() and then conversion to a string with expr_text(). In this instance there doesn't seem to be an advantage to going beyond the base R deparse(substitute(arg)) (as I'm not doing anything further with the captured argument other than using it as character vector in a tibble) but for other uses what enquo does may be necessary/desirable.

1 Like

Ah! Right. That actually solves an additional problem (that the user needs to pass m_gamm$gam is an implementation detail of how mgcv::gamm() returns the model as two sides of the same coin). It would be neater to capture the model name (m_gamm) than the thing they have to pass (for S3 dispatch to work).

With your foo() I can do:

arg <- foo(m_gamm$gam)
as.character(arg[[1]])

yielding

r$> as.character(arg[[1]])
[1] "m_gamm"

Thanks!

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.