Protect yourself from equals assignment!

A man sits at a table with a banner that has R's left assignment operator and underneath says 'change my mind'.

tl;dr

I present you a function that warns if an R script contains the Assignment Operator That Shall Not Be Named.

Assign of the times

So, it’s been confirmed with extremely robust and objective evidence: the left-assignment arrow (x <- 1) is better than equals (x = 1) for assignment in R.1

So, unless you hate democracy, you should protect yourself from aberrant code that uses the cursed symbol.

But what if a nefarious colleague still sends you their scuffed code?

Assignment refinement

I’ve created the appraise_assignment() function that will peek at a suspect script and warn you if it contains the foul mark.

appraise_assignment <- function(file, destroy = FALSE) {
  
  trees <- lintr::get_source_expressions(file)  # parse code
  expr_len <- length(trees[[1]])  # we want the final element
  tokens <-  # extract tokens used in script
    trees$expressions[[expr_len]]$full_parsed_content[, "token"]
  
  if (any(tokens == "EQ_ASSIGN")) {  # '='
    warning("\nme = 'disgusted'\n") 
    if (destroy == TRUE) {
      answer <- readlines("Destroy file? y/n")
      if (answer == "y") cat("Have mercy! This time...")
    }
  } else if (any(tokens == "RIGHT_ASSIGN")) {  # '<-'
    cat("'unorthodox' -> you\n")
  } else if (any(tokens == "LEFT_ASSIGN")) {  # '->'
    cat("you <- 'hero'\n")
  } else {
    cat("anyway(assignment(is(even('what'))))\n")
  }
  
}

It uses Jim Hester’s {lintr} package to parse R expressions and identify each ‘token’ that’s used (maths symbols, special operators, variables, etc).

In particular, it spots the token called EQ_ASSIGN, which is when = is used in the context of assignment.

I saw the assign

For demonstration purposes, I’ve written four temporary files containing left assign (<-), right assign (->, inferior mirror-copy of <-), equals (=), and no assignment at all.2 Our function will catch even a single deviation, even if proper assignment has also been used.

temp <- tempdir()  # temp location to store files
purrr::walk2(
  c("x <- 1", "x <- 1; y -> 1", "x <- 1; y = 1", "x"),
  c("left", "right", "equals", "none"),
  ~writeLines(.x, file.path(temp, paste0(.y, ".R")))
)

First, let’s pass the file containing the unquestionably correct assignment operator.

appraise_assignment(file.path(temp, "left.R"))
## you <- 'hero'

Right-assignment is left-assignment’s less-handsome sibling.

appraise_assignment(file.path(temp, "right.R"))
## 'unorthodox' -> you

Hold steady…

appraise_assignment(file.path(temp, "equals.R"))
## Warning in appraise_assignment(file.path(temp, "equals.R")): 
## me = 'disgusted'

Phew, we got a warning, so we know the file is dangerous and should never be opened.

In fact, if you set the argument destroy = TRUE in appraise_assignment(), you’ll be prompted to irrecoverably annihilate the rotten file forever.3

For completeness, is it really an R script if it doesn’t contain any assignment at all?

appraise_assignment(file.path(temp, "none.R"))
## anyway(assignment(is(even('what'))))

Assigning off

In conclusion, some assignment operators were created more equal than others. See Colin Fay’s round-up to learn more about the history and plethora of these symbols (and be happy that the underscore is no longer legitimate).

Anyway, welcome to the best timeline, where we all recognise <- unequivocally as the champion and = can get absolutely rekt.

If I had one wish though, it would be to make the left-assign arrow even more powerful. How about making it really long? 23 hyphens seems sufficiently dominant.

x <----------------------- 1
x
## [1] 1

It’s a really long arrow, so I call it ‘the spear’.4 I look forward to its adoption by R Core.


  1. Actually, I don’t really care which one you use, but that’s less of a funny take. I prefer the left assignment operator because look! It’s a little arrow! Quirky! Esoteric! An extra keystroke to exercise your fingers!↩︎

  2. We do not talk about <<-.↩︎

  3. Well, not really, because I don’t want you to delete any of your files. But rest assured I’ve included file.remove() in my local version of the function and I’m not afraid to use it.↩︎

  4. If you’re wondering how this works, R evaluates this as <- and then a whole bunch of ‘negative’ symbols. They cancel out If you have an odd number of them.↩︎