Why does rlang::mode_node_car fail in this example?


#1

I’m trying to figure why rlang::mut_node_car fails in some code I am working on.

I have a simple example of a quosue

q <- rlang::quo( a + b)

and I want to use mut_nod_car to change the ‘+’ to a ‘-’

The code in this reprex works as expected

suppressPackageStartupMessages(library(tidyverse))
q <- rlang::quo(a + b)
# what q looks like to start with
q
#> <quosure: frame>
#> ~a + b
# note q is setup for addition
q
#> <quosure: frame>
#> ~a + b
a <- 2
b <- 3
# and evaluating it produces 5, as expected
rlang::eval_tidy(q)
#> [1] 5


## this works and changed + to -
change_to_minus <- function(q) {
    #q2 <- q[[2]]
    q2 <- rlang::f_rhs(q)
    car <- rlang::node_car(q2)
    cdr <- rlang::node_cdr(q2)
    mod_car <- function(car) {
        if (rlang::is_symbol(car)) {
            if (car == rlang::sym("+")) {
                rlang::mut_node_car(q2, rlang::sym("-"))
            }
        }
    }
    mod_car(car)
}
change_to_minus(q)
# note that the '+' is now a '-' as expected
q
#> <quosure: frame>
#> ~a - b
# and evaluating it produces -1, as expected
rlang::eval_tidy(q)
#> [1] -1

However this code fail so badly it can’t be run in a reprex. Except for being
wrapped in a function is it is above it seems to me to be the same code.

why does this code fail???

q <- rlang::quo(a + b)
# what q looks like to start with
# this does not work, why ???
rhs <- rlang::f_rhs(q)
car <- rlang::node_car(rhs)
if (rlang::is_symbol(car)) {
	rlang::mut_node_car(car, rlang::sym("-"))
}

# note that garbage has been inserted into q
# also because the rlang:node functions do
# not do any checking this result will eventually
# crash the R session because something, no
# doubt, has been corrupted
q

The check of q at the end produces

q
<quosure: global>
~a 0�� b

Note that the rlang::mut functions don’t do any checking and will mindlessly write over any memory they happed to be pointed which is probably why the reprex fails. This code also corrupts the R Studio session it is running when a reprex isn’t used

So why does the the code in the second example fails???

Thanks for any insights…
Dan


#2

I’ve just starting to dive in to tidy_eval but does the following do what you expect?

I noticed in your first example you’re modifying the entire quosure but you are calling it on car (operator? still learning this terminology) in your second example.


q <- rlang::quo(a+b)
rhs <- rlang::f_rhs(q)
car <- rlang::node_car(rhs)
rlang::mut_node_car(rhs, rlang::sym("-"))
q
#> <quosure: frame>
#> ~a - b

#3

Ah, I didn’t spot that difference!!! I think you’re right. I’ll try it as soon as I get back .

Thanks


#4

That fixed it. Thanks again.

BTW the rhs of a quosure is an expression which you can get a car and cdr from.

The car and cdr are, in effect, lists of, at least in theory, any kinds of elements. In practice the lists will contain atomic, symbol, or language objects.

The cdr can contain atomic, symbol, or language objects (any mix of them)

In most cases the car contains only a single symbol object which is the name of a function, but it also can contain multiple atomic, symbol or language objects.

The language objects are used to drill further into the into the AST for the quosure.


#5

Hi Dan,

If you’re question’s been answered, would you mind selecting the solution so that we:

  1. Know that it’s been solved; and
  2. Someone in the future knows what the solution was?

Thanks!


#6

Sure. How do a select a solution? I don’t see a “problem solved” button or anything like that.


#7

Taking this one straight from the meta-discourse site:


It’s the little checkbox that only the OP (you, in this case) can see.