Actually, this is not a design flaw, and it is not because of internals or performance.
It comes simply from the fact that functions in Python are first-class objects, and not only a piece of code.
As soon as you think of it this way, then it completely makes sense: a function is an object being evaluated on its definition; default parameters are kind of "member data" and therefore their state may change from one call to the other - exactly as in any other object.
In any case, Effbot has a very nice explanation of the reasons for this behavior in Default Parameter Values in Python.
I found it very clear, and I really suggest reading it for a better knowledge of how function objects work.
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.
Best Answer
This is just guesswork on my part, but:
1. It's hard to implement this in a single-pass compiler
Lua's bytecode compiler is implemented as a single-pass recursive descent parser that immediately generates code. It does not parse to a separate AST structure and then in a second pass convert that to bytecode.
This forces some limitations on the grammar and semantics. In particular, anything that requires arbitrary lookahead or forward references is really hard to support in this model. This means assignments are already hard to parse. Given something like:
When you're parsing
foo.bar.baz
, you don't realize you're actually parsing an assignment until you hit the=
after you've already parsed and generated code for that. Lua's compiler has a good bit of complexity just for handling assignments because of this.Supporting self-assignment would make that even harder. Something like:
Needs to get translated to:
But at the point that the compiler hits the
=
, it's already forgotten aboutfoo.bar.baz
. It's possible, but not easy.2. It may not play nice with the grammar
Lua doesn't actually have any statement or line separators in the grammar. Whitespace is ignored and there are no mandatory semicolons. You can do:
Or:
And Lua is equally happy with both. Keeping a grammar like that unambiguous is tricky. I'm not sure, but self-assignment operators may make that harder.
3. It doesn't play nice with multiple assignment
Lua supports multiple assignment, like:
It's not even clear to me what it would mean if you tried to do:
You could limit self-assignment operators to single assignment, but then you've just added a weird corner case people have to know about.
With all of this, it's not at all clear that self-assignment operators are useful enough to be worth dealing with the above issues.