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) {
tokens <- getParseData(parse(file))[["token"]]
if (any(tokens == "EQ_ASSIGN")) { # if '='
warning("\nme = 'disgusted'")
if (destroy == TRUE) {
answer <- readline("Destroy file? y/n: ")
if (answer == "y") cat("Have mercy! This time...")
}
} else if (any(tokens == "RIGHT_ASSIGN")) { # if '<-'
cat("'unorthodox' -> you\n")
} else if (any(tokens == "LEFT_ASSIGN")) { # if '->'
cat("you <- 'hero'\n")
} else {
cat("anyway(assignment(is(even('what'))))\n")
}
}
Basically, we parse()
an input file and then the function uses getParseData()
to extract ‘tokens’ (i.e. maths symbols, special operators, variables, etc) from the R expressions within.
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 (->
), equals (=
), and no assignment at all.2 Our function will catch even a single deviation in a given file.
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")))
)
list.files(temp, pattern = ".R$")
## [1] "equals.R" "left.R" "none.R" "right.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.
Session info
## ─ Session info ───────────────────────────────────────────────────────────────
## setting value
## version R version 4.1.0 (2021-05-18)
## os macOS Big Sur 10.16
## system x86_64, darwin17.0
## ui X11
## language (EN)
## collate en_GB.UTF-8
## ctype en_GB.UTF-8
## tz Europe/London
## date 2021-09-26
##
## ─ Packages ───────────────────────────────────────────────────────────────────
## package * version date lib source
## blogdown 1.4 2021-07-23 [1] CRAN (R 4.1.0)
## bookdown 0.23 2021-08-13 [1] CRAN (R 4.1.0)
## bslib 0.2.5.1 2021-05-18 [1] CRAN (R 4.1.0)
## callr 3.7.0 2021-04-20 [1] CRAN (R 4.1.0)
## cli 3.0.1 2021-07-17 [1] CRAN (R 4.1.0)
## crayon 1.4.1 2021-02-08 [1] CRAN (R 4.1.0)
## cyclocomp 1.1.0 2016-09-10 [1] CRAN (R 4.1.0)
## desc 1.3.0 2021-03-05 [1] CRAN (R 4.1.0)
## digest 0.6.27 2020-10-24 [1] CRAN (R 4.1.0)
## evaluate 0.14 2019-05-28 [1] CRAN (R 4.1.0)
## htmltools 0.5.1.1 2021-01-22 [1] CRAN (R 4.1.0)
## jquerylib 0.1.4 2021-04-26 [1] CRAN (R 4.1.0)
## jsonlite 1.7.2 2020-12-09 [1] CRAN (R 4.1.0)
## knitr 1.34 2021-09-09 [1] CRAN (R 4.1.0)
## lazyeval 0.2.2 2019-03-15 [1] CRAN (R 4.1.0)
## lintr 2.0.1 2020-02-19 [1] CRAN (R 4.1.0)
## magrittr 2.0.1 2020-11-17 [1] CRAN (R 4.1.0)
## processx 3.5.2 2021-04-30 [1] CRAN (R 4.1.0)
## ps 1.6.0 2021-02-28 [1] CRAN (R 4.1.0)
## purrr 0.3.4 2020-04-17 [1] CRAN (R 4.1.0)
## R6 2.5.1 2021-08-19 [1] CRAN (R 4.1.0)
## remotes 2.4.0 2021-06-02 [1] CRAN (R 4.1.0)
## rex 1.2.0 2020-04-21 [1] CRAN (R 4.1.0)
## rlang 0.4.11 2021-04-30 [1] CRAN (R 4.1.0)
## rmarkdown 2.10 2021-08-06 [1] CRAN (R 4.1.0)
## rprojroot 2.0.2 2020-11-15 [1] CRAN (R 4.1.0)
## rstudioapi 0.13 2020-11-12 [1] CRAN (R 4.1.0)
## sass 0.4.0 2021-05-12 [1] CRAN (R 4.1.0)
## sessioninfo 1.1.1 2018-11-05 [1] CRAN (R 4.1.0)
## stringi 1.7.4 2021-08-25 [1] CRAN (R 4.1.0)
## stringr 1.4.0 2019-02-10 [1] CRAN (R 4.1.0)
## withr 2.4.2 2021-04-18 [1] CRAN (R 4.1.0)
## xfun 0.26 2021-09-14 [1] CRAN (R 4.1.0)
## xml2 1.3.2 2020-04-23 [1] CRAN (R 4.1.0)
## xmlparsedata 1.0.5 2021-03-06 [1] CRAN (R 4.1.0)
## yaml 2.2.1 2020-02-01 [1] CRAN (R 4.1.0)
##
## [1] /Library/Frameworks/R.framework/Versions/4.1/Resources/library
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!↩︎
We do not talk about
<<-
.↩︎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.↩︎In other words, R evaluates this as an object,
x
, being assigned a numeric value that has an odd number of ‘negative’ symbols that cancel each other out.↩︎