I don't think Josh's answer is correct.
Well, it would be right if <- were on the call stack in your example. But this is not so.
A short summary: the usual evaluation of the function R treats the arguments as promises, which are evaluated lazily with ease. This means in the following call:
foo(bar(baz))
bar(baz) is evaluated inside foo (if at all). Therefore, if we check the call stack inside bar , like this:
bar = function (x) { sys.calls() }
... then it looks like this:
[[1]] foo(bar(baz)) [[2]] bar(baz)
Alas, as you noted, <- (and = ) is not a normal function, its primitive ( BUILTINSXP ). In fact, defined in source R , it looks like this:
{"<-", do_set, 1, 100, -1, {PP_ASSIGN, PREC_LEFT, 1}},
Take a look at the fourth argument: 100 . The comment before this code explains what the numbers mean. Here's the relevant part, explaining the leftmost digit:
Z = 1 talks about evaluating the arguments before the call ( BUILTINSXP )
This means that the following code: a call to bar(baz) is evaluated before the assignment:
`<-`(x, bar(baz))
This is why <- does not appear in sys.calls() : this is not the current call. It is called after the completion of bar .
Theres a way around this limitation: you can override <- / = in R code. If you do this, it will behave like a normal R function:
`<-` = function (lhs, rhs) { name = as.name(deparse(substitute(lhs), backtick = true)) rhs
However, be careful that this entails an unbearable performance hit for each subsequent assignment within the area where the <- redefined: in fact, it makes the assignment about 1000 (!!!) slower. This is usually not acceptable.