3 + 3[1] 6
== operator in RI found a bug with the == operator in R!
August 6, 2019
One of the cool things about working on gradethis (grader need to be renamed) is that we end up doing things that aren’t common in R (i.e., grading and comparing code).
I discovered an inconsistency with the == operator when comparing (long) R expressions.
In R, you can create an expression using the quote() function. This is essentially the code that R will execute. It is similar to the “string” that will be executed, but an actual string in R will return a string, not a command or set of instructions that R will execute.
Compare:
Which will return the executed result of 3 + 3 and
which will return the string "3 + 3"
with:
which returns the expression 3 + 3 that is the instruction to R without actually evaluating it.
If we wanted to evaluate the expression, we can call eval.
You can read more about expressions in the Expressions Chapter in Advanced R.
The bug was detected in gradethis where we want to compare student submitted code with the instructor solution. There are multiple steps in the comparison process, but the first step is to simply check if the two bits of code are the same. That way, we can stop there and not have to go through the process to detect where the actual differences are.
The comparison code was originally written to use == to compare the expressions.
Garrett Grolemund put in a bunch of examples that show some weird behaviour. I initially thought it had to do with name spacing the function name, or after using the : notation to select columns in a dataframe via tidyselect.
When the two expressions are the same, we get TRUE as expected
[1] TRUE
But when we change the values for na.rm, we also get TRUE when the expressions are not the same.
[1] FALSE
But it seems if we get rid of the tidyselect column selector, we get the correct result.
[1] FALSE
I brought this up on our daily shiny-core stand-ups and Winston Chang thought it may have something to do with the deparse function since it doesn’t actually matter what the expressions being compared are.
[1] FALSE
You can see Winston’s comment and link to R code in question here.
Pretty much when == is used to compare expressions, the expressions are passed through deparse. When deparse is run on an expression, it breaks it up into vectors that are 60L characters long, which is fine, but the R bug is when the comparison is only performed with the first element of the vector. That’s why only the end of the expressions seem to “not matter”.
I reported the findings to the r-devel mailing list
Where, even after botching my first listserv submission, I got a response from Martin Maechler (R-core)
Looking at that and its context, I think we (R core) should reconsider that implementation of ‘==’ which indeed does about the same thing as deparse {which also truncates at some point by default; something very very reasonable for error messages, but undesirable in other cases}.
But I think it’s fair expectation that comparing calls [“language”] with ‘==’ should compare the full call’s syntax even if that may occasionally be very long.
So it is actually a behavior that will get patched one day.
We ended up making changes to gradethis by using identical() while comparing quoted expressions.
[1] FALSE
Using identical() is a much better way when we are comparing code and results, because == will return a matrix when comparing 2 dataframes where using all has problems when there are NA missing values.
We want to see if the 2 vectors are the same
We can remove missing values, but now when either the user code or solution code does contains an NA it gets ignored.
Now, we nudge toward using identical and raise a warning when we detect ==.
Does Donald Knuth owe me a dollar now?