What are the differences between the assignment operators =
and <-
in R?
I know that operators are slightly different, as this example shows
x <- y <- 5 x = y = 5 x = y <- 5 x <- y = 5 # Error in (x <- y) = 5 : could not find function "<-<-"
But is this the only difference?
The operators <- and = assign into the environment in which they are evaluated. The operator <- can be used anywhere, whereas the operator = is only allowed at the top level (e.g., in the complete expression typed at the command prompt) or as one of the subexpressions in a braced list of expressions.
Compare <- and = in the global environment in R The inbuilt function environment() returns the name of the current environment. We can see below that there is no difference between using <- or =.
The Google R style guide prohibits the use of “=” for assignment. Hadley Wickham's style guide recommends “<-” If you want your code to be compatible with S-plus you should use “<-”
As you all know, R comes from S. But you might not know a lot about S (I don't). This language used <- as an assignment operator. It's partly because it was inspired by a language called APL, which also had this sign for assignment.
The difference in assignment operators is clearer when you use them to set an argument value in a function call. For example:
median(x = 1:10) x ## Error: object 'x' not found
In this case, x
is declared within the scope of the function, so it does not exist in the user workspace.
median(x <- 1:10) x ## [1] 1 2 3 4 5 6 7 8 9 10
In this case, x
is declared in the user workspace, so you can use it after the function call has been completed.
There is a general preference among the R community for using <-
for assignment (other than in function signatures) for compatibility with (very) old versions of S-Plus. Note that the spaces help to clarify situations like
x<-3 # Does this mean assignment? x <- 3 # Or less than? x < -3
Most R IDEs have keyboard shortcuts to make <-
easier to type. Ctrl + = in Architect, Alt + - in RStudio (Option + - under macOS), Shift + - (underscore) in emacs+ESS.
If you prefer writing =
to <-
but want to use the more common assignment symbol for publicly released code (on CRAN, for example), then you can use one of the tidy_*
functions in the formatR
package to automatically replace =
with <-
.
library(formatR) tidy_source(text = "x=1:5", arrow = TRUE) ## x <- 1:5
The answer to the question "Why does x <- y = 5
throw an error but not x <- y <- 5
?" is "It's down to the magic contained in the parser". R's syntax contains many ambiguous cases that have to be resolved one way or another. The parser chooses to resolve the bits of the expression in different orders depending on whether =
or <-
was used.
To understand what is happening, you need to know that assignment silently returns the value that was assigned. You can see that more clearly by explicitly printing, for example print(x <- 2 + 3)
.
Secondly, it's clearer if we use prefix notation for assignment. So
x <- 5 `<-`(x, 5) #same thing y = 5 `=`(y, 5) #also the same thing
The parser interprets x <- y <- 5
as
`<-`(x, `<-`(y, 5))
We might expect that x <- y = 5
would then be
`<-`(x, `=`(y, 5))
but actually it gets interpreted as
`=`(`<-`(x, y), 5)
This is because =
is lower precedence than <-
, as shown on the ?Syntax
help page.
What are the differences between the assignment operators
=
and<-
in R?
As your example shows, =
and <-
have slightly different operator precedence (which determines the order of evaluation when they are mixed in the same expression). In fact, ?Syntax
in R gives the following operator precedence table, from highest to lowest:
… ‘-> ->>’ rightwards assignment ‘<- <<-’ assignment (right to left) ‘=’ assignment (right to left) …
But is this the only difference?
Since you were asking about the assignment operators: yes, that is the only difference. However, you would be forgiven for believing otherwise. Even the R documentation of ?assignOps
claims that there are more differences:
The operator
<-
can be used anywhere, whereas the operator=
is only allowed at the top level (e.g., in the complete expression typed at the command prompt) or as one of the subexpressions in a braced list of expressions.
Let’s not put too fine a point on it: the R documentation is wrong. This is easy to show: we just need to find a counter-example of the =
operator that isn’t (a) at the top level, nor (b) a subexpression in a braced list of expressions (i.e. {…; …}
). — Without further ado:
x # Error: object 'x' not found sum((x = 1), 2) # [1] 3 x # [1] 1
Clearly we’ve performed an assignment, using =
, outside of contexts (a) and (b). So, why has the documentation of a core R language feature been wrong for decades?
It’s because in R’s syntax the symbol =
has two distinct meanings that get routinely conflated (even by experts, including in the documentation cited above):
=
operator it performs no action at runtime, it merely changes the way an expression is parsed.So how does R decide whether a given usage of =
refers to the operator or to named argument passing? Let’s see.
In any piece of code of the general form …
‹function_name›(‹argname› = ‹value›, …) ‹function_name›(‹args›, ‹argname› = ‹value›, …)
… the =
is the token that defines named argument passing: it is not the assignment operator. Furthermore, =
is entirely forbidden in some syntactic contexts:
if (‹var› = ‹value›) … while (‹var› = ‹value›) … for (‹var› = ‹value› in ‹value2›) … for (‹var1› in ‹var2› = ‹value›) …
Any of these will raise an error “unexpected '=' in ‹bla›”.
In any other context, =
refers to the assignment operator call. In particular, merely putting parentheses around the subexpression makes any of the above (a) valid, and (b) an assignment. For instance, the following performs assignment:
median((x = 1 : 10))
But also:
if (! (nf = length(from))) return()
Now you might object that such code is atrocious (and you may be right). But I took this code from the base::file.copy
function (replacing <-
with =
) — it’s a pervasive pattern in much of the core R codebase.
The original explanation by John Chambers, which the the R documentation is probably based on, actually explains this correctly:
[
=
assignment is] allowed in only two places in the grammar: at the top level (as a complete program or user-typed expression); and when isolated from surrounding logical structure, by braces or an extra pair of parentheses.
In sum, by default the operators <-
and =
do the same thing. But either of them can be overridden separately to change its behaviour. By contrast, <-
and ->
(left-to-right assignment), though syntactically distinct, always call the same function. Overriding one also overrides the other. Knowing this is rarely practical but it can be used for some fun shenanigans.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With