What are primitive, internal, builtin, and special functions?

2.7k views Asked by At

I have seen that some functions that call C-code are described as primitive, internal, builtin, or special. What are these functions?

1

There are 1 answers

10
Richie Cotton On

This question conflates two distinct concepts. special and builtin are two of the three types of function (along with closures), as determined by typeof.

From R-ints, 1.5 Argument evaluation

being a special/builtin is separate from being primitive or .Internal: quote is a special primitive, + is a builtin primitive, cbind is a special .Internal and grep is a builtin .Internal.

Builtin vs. Special

If a function calls C-code, builtin/special refers to whether or not its arguments are evaluated before being passed to the C-code.

From R-lang, 2.1.7 Builtin objects and special forms

Builtin functions have all their arguments evaluated and passed to the internal function, in accordance with call-by-value, whereas special functions pass the unevaluated arguments to the internal function.

Internal vs. Primitive

.Internal and .Primitive refer to the interface used to call C-code. Internal is the standard approach, since you can check arguments in R-code before you call the C-code. Primitive is higher performance, but does not allow any R-code in the function.

From R-ints, 2 .Internal vs .Primitive

C code compiled into R at build time can be called directly in what are termed primitives or via the .Internal interface, which is very similar to the .External interface except in syntax. More precisely, R maintains a table of R function names and corresponding C functions to call, which by convention all start with ‘do_’ and return a SEXP. This table (R_FunTab in file src/main/names.c) also specifies how many arguments to a function are required or allowed, whether or not the arguments are to be evaluated before calling, and whether the function is ‘internal’ in the sense that it must be accessed via the .Internal interface, or directly accessible in which case it is printed in R as .Primitive.

Functions using .Internal() wrapped in a closure are in general preferred as this ensures standard handling of named and default arguments.

However, for reasons of convenience and also efficiency (as there is some overhead in using the .Internal interface wrapped in a function closure), the primitive functions are exceptions that can be accessed directly.