[R] chaining closure arguments on-the-fly

Duncan Murdoch murdoch@dunc@n @end|ng |rom gm@||@com
Sun Jun 21 22:48:22 CEST 2020


On 21/06/2020 11:37 a.m., Benjamin Tyner wrote:
> On 6/20/20 5:04 PM, Duncan Murdoch wrote:
>> I think you effectively did that in your original post (all but
>> encapsulating the expression in a function), so yes, it's possible.
>> However, it's a really bad idea.  Why use non-standard evaluation when
>> standard evaluation is fine?  Standard evaluation follows some well
>> defined rules, and is easy to reason about.  NSE follows whatever
>> rules it wants, so it's really hard for users to follow.  For example,
>> assuming you had the g() you want, what would this give?
>>
>> z <- 3
>> f(x = z, y = g(z))
>>
>> You can't possibly know that without knowing whether there's a local
>> variable in f named z that is created before y is evaluated.
>>
>> Duncan Murdoch
>>
>>
> Very good point, and I agree it's best to use standard evaluation
> whenever possible. The role of g would essentially be to modify one or
> more elements of f's formals in-place. For example calling:
> 
>      f(x = 3, y = g(expr))
> 
> would be equivalent to calling a function fm:
> 
>      fm(x = 3)
> 
> where the body of fm is identical to that of f, but:
> 
>       > formals(fm)
>      $x
> 
> 
>      $y
>      expr
> 
> though I expect g would be non-trivial to code up robustly.

I would still say that's a bad idea.  The defaults for a function's 
arguments are decided by the function's author, so it makes sense for 
them to be evaluated in the evaluation frame of the function.  The 
actual arguments given to the function are decided by whoever calls it, 
so they should be evaluated in the caller's environment.  That's 
standard evaluation.

What you're trying to do mixes up these things:  you want the caller to 
be able to evaluate things that depend on function internals.  That is 
bad, because it means the function can no longer be a black box that 
does what it is documented to do:  it also needs to do it in exactly the 
way it did when the caller wrote g(expr), or that could give a garbage 
value.  Effectively you've broken the encapsulation that functions give 
you.  You've made f() part of whatever function calls it (but imposed 
the rule that says the source of f() can't be modified, which makes no 
sense).

Duncan Murdoch



More information about the R-help mailing list