<- function(file, destroy = FALSE) {
appraise_assignment
<- getParseData(parse(file))[["token"]]
tokens
if (any(tokens == "EQ_ASSIGN")) { # if '='
warning("\nme = 'disgusted'")
if (destroy == TRUE) {
<- readline("Destroy file? y/n: ")
answer 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")
}
}
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.
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.
<- tempdir() # temp location to store files
temp
::walk2(
purrrc("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.
<----------------------- 1
x 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.
Environment
Session info
Last rendered: 2024-07-14 20:15:17 BST
R version 4.4.0 (2024-04-24)
Platform: aarch64-apple-darwin20
Running under: macOS Ventura 13.2.1
Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.0
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: Europe/London
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
loaded via a namespace (and not attached):
[1] crayon_1.5.3.9000 vctrs_0.6.5 cli_3.6.3.9000 knitr_1.46
[5] rlang_1.1.4.9000 xfun_0.43 rex_1.2.1 processx_3.8.4
[9] purrr_1.0.2 xmlparsedata_1.0.5 data.table_1.15.4 jsonlite_1.8.8
[13] glue_1.7.0 backports_1.4.1 htmltools_0.5.8.1 ps_1.7.6
[17] fansi_1.0.6 rmarkdown_2.26 tibble_3.2.1 evaluate_0.23
[21] fastmap_1.2.0 yaml_2.3.8 lifecycle_1.0.4 cyclocomp_1.1.1
[25] compiler_4.4.0 lintr_3.1.2 pkgconfig_2.0.3 htmlwidgets_1.6.4
[29] rstudioapi_0.16.0 digest_0.6.35 R6_2.5.1 utf8_1.2.4
[33] pillar_1.9.0 callr_3.7.6 magrittr_2.0.3 tools_4.4.0
[37] withr_3.0.0 lazyeval_0.2.2 xml2_1.3.6 remotes_2.5.0
[41] desc_1.4.3
Footnotes
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.↩︎