class of expr and exprs are different in rlang in R ! Why?

506 Views Asked by At

I am not sure if this has been asked here, But I am very confused here. I am reading this awesome book called Advanced R by Hadley Wickham from here.

There is function called cement that has been described here, I have modified it little bit and trying to understand it.

library(rlang)
cement1 <- function(x) {
  dots <- expr(x)
  print(class(dots))
  #paste(expr_name(x))
}

cement2 <- function(y,z) {
  dots <- exprs(y,z)
  print(class(dots))
  #paste(purrr::map(dots, expr_name), collapse = " ")
}

Running the above cement1 without any parameter returns me the class of dots as "name".

However, when I run the cement2 function with additional parameter, the class returns "list", {simply putting class(expr(x)) returns "name" whereas class(exprs(x)) returns "list"}.

I am not getting my head around this as why it is printing different class returned by expr and exprs. The only difference I thought I knew about them was, one deals with one parameter, other one deals with multiple parameters, but I may be wrong, I might have missed some details.

Original Problem: So, it all started by running these two functions separately by removing the comments section in the code for both cement1 and cement2, when I run the functions Below are the output returned by them:

cement1(Hello) #Returns , Error in type_of(.x) : object 'Hello' not found 
cement2(Hello) #Works very well and returns, [1] "y z"

So I tried to find the reason why cement1 failed and then printed their classes and that is when I realized , expr and exprs return different classes.

My question is:

1) Are they by design, if yes then why? Or, I am doing some horrible mistake, which I am currently unable to see.

2) Does cement1 can't work this if not , what is the correct way?

I am sorry for too long sentences, My first language is not English, hence If anything silly is there, Please let me know I shall correct it. I hope this is not a duplicate, I tried to find the answer but could not found by my own.

Thanks for any help.

R Version: 3.4.2 rlang: 0.2.0

1

There are 1 best solutions below

0
On BEST ANSWER

1) Yes, the return values of expr and exprs differ by design. From the ?expr help page:

enexpr() and expr() capture a single raw expression.

enexprs() and exprs() capture a list of raw expressions including expressions contained in ....

2) expr_name() expects a quoted expression, such as what's produced by expr(). So, you need to modify your cement1 to call expr_name() on dots, not x. You can also remove paste because you are not concatenating anything.

cement1 <- function(x) {
  dots <- expr(x)
  # print(class(dots))      ## Commented out for clarity
  expr_name(dots)           ## The input to expr_name is now effectively expr(x)
}
cement1( Hello )
# "x"

Your function cement2 basically calls expr_name() on every element of the list returned by exprs(), then concatenates the results into a single string.

2a) Now that we got your cement1 working, we can improve it further. Currently, the function doesn't make any use of its input argument x. expr() simply captures the unevaluated expression, and this expression will always be x, regardless of what you name your argument:

cement1.1 <- function( completelyIgnoredName ) {
  dots <- expr(x)
  expr_name(dots)
}
cement1.1( Hello )
# "x"

However, if you replace expr() with enexpr(), the function will substitute the expression provided as the function's argument and capture that instead:

cement1.2 <- function(x) {
  dots <- enexpr(x)
  expr_name(dots)
}
cement1.2( Hello )
# "Hello"