Hacker News new | past | comments | ask | show | jobs | submit login

Sure! If you want to experiment with this, it's pretty easy to "reverse engineer" that original form. Just use quote, and convert to a list (you need to do that because expressions will pretty print by default using the same sugar!), to see the internal structure:

   > as.list(quote(if (1 > 2) 3 else 4));
   [[1]]
   `if`

   [[2]]
   1 > 2

   [[3]]
   [1] 3

   [[4]]
   [1] 4
Okay, let's try this:

   > `if`(1 > 2, 3, 4)
   [1] 4
And to make sure that it really does evaluate only the correct branch:

   > `if`(1 > 2, cat(3), cat(4))
   4
Now something more interesting:

   > f <- as.list(quote(function(x, y=1) { x + y }));
   > f
   [[1]]
   `function`

   [[2]]
   [[2]]$x


   [[2]]$y
   [1] 1


   [[3]]
   {
       x + y
   }

   [[4]]
   function(x, y=1) { x + y }
This last entry is probably confusing, because it looks recursive. However, it's not the function itself - it's the srcref (basically, metadata about where the code came from, used e.g. by debugger to report line numbers) - it just pretty-printed itself like the function it is for. We can ignore it, though. Otherwise there are two arguments here - first one is a pairlist with named elements, one for each argument, and values are the default values for those arguments (if present). Second argument is the function body, which is itself an expression. We can look at that:

   > as.list(f[[3]])
   [[1]]
   `{`

   [[2]]
   x + y
So {} is itself a function! And x+y works as you'd expect:

   > as.list(f[[3]][[2]])
   [[1]]
   `+`

   [[2]]
   x

   [[3]]
   y
Now let's try to do the same ourselves. One catch here is that function() expects the first argument to be a list itself, rather than an expression that evaluates to a list. So we can't do this:

   > `function`(pairlist(x=1, y=2), quote({x + y}))
   Error: invalid formal argument list for "function"
Because the first argument is not itself a pairlist, but a promise of one. So we need to construct the call, thereby evaluating the arguments in advance, and then eval it. Here's the first take, ignoring the function body:

   > eval(call("function", pairlist(x=1, y=2), quote({x + y})))
   function (x = 1, y = 2) 
   {
       x + y
   }
The body we can just rewrite as plain calls:

   > eval(call("function", pairlist(x=1, y=2), quote(`{`(`+`(x, y)))))
   function (x = 1, y = 2) 
   {
       x + y
   }
And just to make sure it does what it should:

   > eval(call("function", pairlist(x=1, y=2), quote(`{`(`+`(x, y)))))(123)
   [1] 125
You might have noticed that I've cheated a bit here by giving each argument a default value - the original didn't have one for the first argument. It's because we need to somehow get a "missing" bit on a list element for that to work, and this makes it a great deal more convoluted - R has an easy way to check for it, but not to set it, other than by omitting arguments in function calls. The easiest way to get it is to quote() a call with one, and then just pull the pairlist out of the expression tree.



That's really awesome, thanks!




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: