Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can R not find the value of an argument through lexical scoping?

I have been reading Hadley Wickham's Advanced R in order to gain a better understanding of the mechanisms of R and how it works behind the scene. I have so far enjoyed it and everything is quite clear. There is one question that occupies my mind for which I have not yet found an explanation. I am quite familiar with the scoping rules of R which determine how values are assigned to FREE VARIABLES. However, I have been grappling with the question of why R cannot find the value of a formal argument through lexical scoping in the first case. Consider the following example:

y <- 4
f1 <- function(x = 2, y) {
  x*2 + y
}

f1(x = 3)

It normally throws an error because I didn't assign a default value for argument y. However, if I create a local variable y in the body of the function it won't throw any error: I also read in Professeur Matloff's book that arguments act like local variables, so that's why this question remains a mystery for me.

f1 <- function(x = 2, y) {
  y <- 4
  x*2 + y
}

f1(x = 3)

And also here there is no error and it is quite clear why:

y <- 2
f2 <- function(x = 2) {
  x*2 + y
}

f2()

Thank you very much in advance.

like image 898
Anoushiravan R Avatar asked Jan 25 '23 06:01

Anoushiravan R


1 Answers

Note that R will only throw the error when you go to use the variable. if you had

f1 <- function(x = 2, y) {
  x*2 + 5
}

f1(x = 3)
# [1] 11

everything would be fine. That's because the parameter is a "promise" which isn't resolved till you actually use it. This allows you to do things like

f1 <- function(x = 2, y=x+5) {
  x*2 + y
}

f1(x = 3)
# [1] 14

Where the y value will actually use the value of x that's passed to the function when the promise is evaluated. Furthermore you can also do

f1 <- function(x = 2, y=z+2) {
  z <- x + 10
  x*2 + y
}

f1(x = 3)
[1] 21

Where y is able to take the value of z that didn't even exist when the function was called. Again this is because the parameter values are promises and are only evaluated when they are actually used. They have access to all the values in the environment when they are evaluated. But note that this only works because default parameter values are evaluated in the context of the function body. This is different than when you pass in a value to a function. In that case the value is evaluated in the calling environment, not the local function body. So you can't do

f1(x = 3, y=z+2)
# Error in f1(x = 3, y = z + 2) : object 'z' not found

The the reason you get the error in your first function is that a value for y does not exist when you try to use it in x*2 + y. Since you've defined y as a parameter, it is no longer a "free" variable and will not be looked up in parent scope. You don't get an error in your second function because you've re-bound the y variable to a local function variable so you are never using the parameter value at all.

If you ran

f1 <- function(x = 2, y) {
  y <- 4
  x*2 + y
}

f1(x = 3, y=200)
# [1] 10

The 2000 basically disappears. You no longer have access to that value after you reassign y. R does not check if a variable exists already before redefining so there is nothing that will try to evaluate the promise value y of the function parameter.

Arguments will act like local variables once the promise has been evaluated.

like image 160
MrFlick Avatar answered Feb 10 '23 04:02

MrFlick