BenBarnes BenBarnes - 3 months ago 24
R Question

Show special primitive functions in call stack

This question prompted the following question: Is there a way to view the special primitive functions that are in the call stack?

For example, create a function that returns the call stack on exit:

myFun <- function(obj){
on.exit(print(sys.calls()))
return(obj)
}


Calling this function and assigning its result to an object using
assign
avoids using special primitive functions:

> assign("myObj",myFun(4))
[[1]]
assign("myObj", myFun(4))

[[2]]
myFun(4)


But using the assignment operator, this gets left out of the stack

> `<-`(myObj, myFun(6))
[[1]]
myFun(6)


Granted, it might not be all that common to want to see the assignment operator in the call stack, but other functions such as
rep
and
log
also get hidden

Answer Source

I don't think there's any way to access calls to primitive functions via the call stack. Here is why.

When a "typical" R function is evaluated:

  1. The supplied arguments are matched to the formal arguments.
  2. A new environment (with a pointer to its enclosing environment) is created, and the formal arguments are assigned into it.
  3. The body of the function is evaluated in the newly created environment.

The chain of enclosing environments that is built up when function calls are nested within one another is the "call stack" or "frame stack" to which sys.calls(), sys.frames() and the like provide some access.

My strong suspicion is that calls to primitive functions don't appear on the call stack because no R-side environment is created during their evaluation. No environment is created, so no environment appears on the call stack.

For some more insight, here's how John Chambers describes the evaluation of primitive functions on page 464 of Software for Data Analysis:

Evaluation of a call to one of these functions starts off in the usual way, but when the evaluator discovers that the function object is a primitive rather than a function defined in R, it branches to an entirely different computation. The object only appears to be a function object with formal arguments and a call to the function .Primitive() with a string argument. In reality, it essentially contains only an index into a table that is part of the C code implementing the core of R. The entry of the table identifies a C routine in the core that is responsible for evaluating calls to this specific primitive. The evaluator will transfer control to that routine, and expects the routine to return a C-language pointer to the R object representing the value of the call.