6

Why there is no error reported when b is not supplied but required inside the function? Thanks!

f2 <- function(a,b) {a[b]}; f2(a=rep(1, 2))

I understand that there is no error in this function:

f <- function(x) {
  10
}
f(stop("This is an error!"))

due to lazy evaluation But this

f <- function(x) {
  force(x)
  10
}
f(stop("This is an error!"))

or this

f <- function(x) {
  x
  10
}
f(stop("This is an error!"))

will produce an error. Because in both cases x is used within the function. Both the above two examples are from http://adv-r.had.co.nz/Functions.html. Since b is also used within f2, should it be necessary to add force inside f2? Thanks!

kevin
  • 174
  • 6
  • 1
    Simpler version, same issue - `f2 <- function(a,b) {a[b]}; f2(a=1)` – thelatemail Apr 26 '18 at 01:59
  • 3
    I'm guessing this is some lazy evaluation quirk that I don't fully understand since if you `debug(f2)` you can see that `b` is returned from `ls()` inside the function's environment. `f2 <- function(a,b) {a[force(b)]}; f2(a=1:2)` also fails once `?force` is used with a '*argument "b" is missing*" error. Source: https://stackoverflow.com/questions/29733257/can-you-more-clearly-explain-lazy-evaluation-in-r-function-operators – thelatemail Apr 26 '18 at 02:16
  • But I think in the link above, the original function f is not **used**. https://stackoverflow.com/questions/29733257/can-you-more-clearly-explain-lazy-evaluation-in-r-function-operators – kevin Apr 26 '18 at 02:36

2 Answers2

6

x[b] returns (a duplicate of) x if b is missing. From the R source:

static SEXP VectorSubset(SEXP x, SEXP s, SEXP call)
{
    R_xlen_t stretch = 1;
    SEXP indx, result, attrib, nattrib;

    if (s == R_MissingArg) return duplicate(x);

https://github.com/wch/r-source/blob/ec2e89f38a208ab02449b706e13f278409eff16c/src/main/subset.c#L169

From the documentation, in which 'empty' means 'missing', not NULL:

An empty index selects all values: this is most often used to replace all the entries but keep the attributes.

Hugh
  • 15,521
  • 12
  • 57
  • 100
3

It has to do with the [ function, not lazy evaluation. You'll get an error if you do the following:

f3 <- function(a,b) {a+b}; f3(a = 1)

Note that since b is not defined, R is interpreting it as if it didn't exist. Try doing:

a <- c(1,1) 
a[]

It seems that the subsetting function ( `[` ) actually takes ... as a parameter. I.e., specifying indices to subset are optional.

David Klotz
  • 2,401
  • 1
  • 7
  • 16
  • Thanks! How about a <- c(1, 1); a[b] without defining b? This will cause an error because b is not defined. But why it is different within a function? – kevin Apr 26 '18 at 02:40
  • 1
    Again, compare what happens when you do this: `f4 <- function(a, ...) {a+...}; f4(1); f4(1,1); f4(1, c(1,1))`. For the subset function, indices are optional (...). When you call `f2` as above, R searches for a variable `b` in the environment, but since it can't match anything, it performs the function with just `a`. This doesn't work in my `f3` example, because addition requires two variables by definition. – David Klotz Apr 26 '18 at 02:54
  • 1
    @DavidKlotz - but `b` does exist in the environment. `debug(f2); f2(a=1)` then `exists("b")` returns `TRUE`. So it exists but is not evaluated, right? – thelatemail Apr 26 '18 at 03:14
  • 1
    It's defined in the function call, but it's still missing: `f2 <- function(a,b) {print(missing(a)); print(missing(b)); a[b]}; f2(a=rep(1, 2))` – David Klotz Apr 26 '18 at 03:34
  • I see, thanks both! Sometimes I might forget to include the second variable b when calling function f2. Is it the best way to always use f <- function(a, b){ a[force(b)] } whenever I am subsetting inside a function for guarding against this risk? – kevin Apr 26 '18 at 03:45
  • 1
    Everything correct here except the part about `...`. If `...` was involved, `\`[\`(a, i = 1, foo = 1)` would work. – Roland Apr 26 '18 at 06:23
  • 1
    The best way is to explicitly check for missingness: `f <- function(a, b) if (missing(b)) stop('b is missing.') else a[b]` – Hugh Apr 26 '18 at 11:59
  • Good point, @Roland. So what's really going on is that the source code for the subset function (quoted by @Hugh) explicitly deals with the case of a missing or empty index. – David Klotz Apr 26 '18 at 13:07