29

I am trying to call the function `function` to define a function in R code.

As we all know™️, `function`is a .Primitive that’s used internally by R to define functions when the user uses the conventional syntax, i.e.

mean1 = function (x, ...) base::mean(x, ...)

But there’s nothing preventing me from calling that primitive directly. Or so I thought. I can call other primitives directly (and even redefine them; for instance, in a moment of madness I overrode R’s builtin `for`). So this is in principle possible.

Yet I cannot get it to work for `function`. Here’s what I tried:

# Works
mean2 = as.function(c(formals(mean), quote(mean(x, ...))))

# Works
mean3 = eval(call('function', formals(mean), quote(mean(x, ...))))

# Error: invalid formal argument list for "function"
mean4 = `function`(formals(mean), quote(mean(x, ...)))

The fact that mean3 in particular works indicates to me that mean4 should work. But it doesn’t. Why?

I checked the definition of the `function` primitive in the R source. do_function is defined in eval.c. And I see that it calls CheckFormals, which ensures that each argument is a symbol, and this fails. But why does it check this, and what does that mean?

And most importantly: Is there a way of calling the `function` primitive directly?


Just to clarify: There are trivial workarounds (this question lists two, and there’s at least a third). But I’d like to understand how this (does not) works.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 2
    Duplicate of https://stackoverflow.com/q/53218422/1968 (I swear I found this afterwards) – Konrad Rudolph Feb 08 '19 at 18:04
  • 1
    Related: https://stackoverflow.com/questions/12982528/how-to-create-an-r-function-programmatically – Artem Sokolov Feb 08 '19 at 18:06
  • 1
    @Moody's answer in the link above points out [rlang::new_function](https://rlang.r-lib.org/reference/new_function.html), which _can_ be called directly as a function and does essentially what `function` does. – Artem Sokolov Feb 08 '19 at 18:08
  • 1
    @ArtemSokolov … sure but that does exactly what my `mean3` definition does. [I’m well aware of workarounds](https://github.com/klmr/functional/blob/c059576ae44e002277f0c9a0a49352080408e54e/__init__.r#L1-L19), I was just curious why it doesn’t seem to work. – Konrad Rudolph Feb 08 '19 at 18:15
  • 1
    Sorry for misunderstanding. Please see my answer below. – Artem Sokolov Feb 08 '19 at 22:02
  • 1
    You probably thought of: `newmean <- do.call( 'function', list(formals(mean), quote(mean(x, ...))))`. Backticks also succeed. – IRTFM May 14 '19 at 18:12

3 Answers3

17

This is because function is a special primitive:

typeof(`function`)
#> [1] "special"

The arguments are not evaluated, so you have actually passed quote(formals(mean)) instead of the value of formals(mean). I don't think there's a way of calling function directly without evaluation tricks, except with an empty formals list which is just NULL.

Lionel Henry
  • 6,652
  • 27
  • 33
  • 2
    I actually tried quoting the formals (as well as using `alist`), yet as you noticed this also doesn’t work. ``typeof(`for`)`` is also “special”, but calling ` ``for`` ` manually works. – Konrad Rudolph Feb 08 '19 at 17:54
  • 5
    Yeah because `for` takes a symbol and two expressions, for which there is parser syntax. There is no explicit parser syntax for creating a pairlist literal, which is what `function()` takes. – Lionel Henry Feb 08 '19 at 17:59
  • 1
    Indeed. I just figured out that you *can* call it with a manually generated `pairlist` — but once again only indirectly because, as you said, there’s no `pairlist` literal syntax. – Konrad Rudolph Feb 08 '19 at 18:01
5

For completeness’ sake, Lionel’s answer hints at a way of calling `function` after all. Unfortunately it’s rather restricted, since we cannot pass any argument definition except for NULL:

mean5 = `function`(NULL, mean(x, ...))
formals(mean5) = formals(mean)

(Note the lack of quoting around the body!)

This is of course utterly unpractical (and formals<- internally calls as.function anyway.)

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
4

After digging a little bit through the source code, here are a few observations:

  1. The actual function creation is done by mkCLOSXP(). This is what gets called by function() {}, by as.function.default() and by .Primitive("function") (a.k.a. `function`)

  2. as.function.default() gets routed to do_asfunction(), which also calls CheckFormals(). However, it directly constructs these formals a few lines above that.

  3. As you pointed out, the other place where CheckFormals() gets called is inside do_function(). However, I don't think do_function() gets called by anything other than .Primitive("function"), so this is the only situation where CheckFormals() is called on the user's input.

  4. CheckFormals() does actually correctly validate a pairlist object.

You can check the last point yourself by running parts of the CheckFormals() function using inline::cfunction

inline::cfunction( c(x="ANY"),
  'Rprintf("is list?: %d\\nTag1 OK?: %d\\nTag2 OK?: %d\\nTag3 NULL?: %d\\n",
     isList(x), TYPEOF(TAG(x)) == SYMSXP, TYPEOF(TAG(CDR(x))) == SYMSXP,
     CDR(CDR(x)) == R_NilValue); return R_NilValue;' )( formals(mean) )

# is list?: 1
# Tag1 OK?: 1
# Tag2 OK?: 1
# Tag3 NULL?: 1

So, somewhere between you passing formals(means) to .Primitive("function") and it getting forwarded to CheckFormals() by do_function(), the argument loses its validity. (I don't know the R source well enough to tell you how that happens.) However, since do_function() is only called by .Primitive("function"), you don't encounter this situation with any other examples.

Artem Sokolov
  • 13,196
  • 4
  • 43
  • 74
  • 2
    You’re spot on as far as your reasoning goes. I think the missing piece to the puzzle is the declaration of `function` in `names.c`: Unlike your own C function, `do_function` doesn’t get called with the evaluated argument; instead, it gets called with *unevaluated* arguments, so what gets passed isn’t a pairlist, it’s a quoted expression that would evaluate to a pairlist. – Konrad Rudolph Feb 09 '19 at 10:07
  • 1
    @KonradRudolph: Do you think this is intended behavior? Neither `do_function`, nor `\`function\``, nor `.Primitive("function")` get used internally (as determined by `grep`-ing the source). So, if a user can't call it directly, who is the function meant for? – Artem Sokolov Feb 09 '19 at 16:53
  • 4
    The function `.Primitive("function")` is called when you evaluate a call to `function` created by the parser into a function object created by the interpreter. Note that those two kinds of objects print identically, which can be confusing. Check the differences between `quote(function(foo, bar) NULL)` and the unquoted version. You can also verify with `.Internal(inspect(quote(function(foo, bar) NULL)))` that the parser correctly creates the formals list in calls to `function`. – Lionel Henry Feb 10 '19 at 10:24