[R] Question about function scope

Duncan Murdoch murdoch@dunc@n @end|ng |rom gm@||@com
Tue Oct 30 21:47:32 CET 2018


On 30/10/2018 4:18 PM, Sebastien Bihorel wrote:
> Thanks Duncan for your quick reply.
> 
> Ideally, I would want bar1 and bar2 to be independent functions, because they are huge in actuality and, as the actual foo function grows, I may end up with 10 different bar# functions. So I would like to separate them from foo as much as possible.

If that's the case, then I think the second solution (passing around 
environments) is a really bad idea.  Functions should not have side 
effects because it makes them harder to understand.  Modifying local 
variables in some other function is a really dangerous side effect, 
especially if both functions are big, because they are already hard to 
understand.

If you really have more callers for bar1() than just foo(), it is even 
worse.

So I'd suggest having bar1 and bar2 return the new values in a list, and 
in foo(), explicitly extract the values you want from the list.  Then if 
in the future you decide that bar1 should also return a 4th value w, or 
you want to rename x to something more meaningful, you don't need to 
check your other foo functions to see if x and w are used in the same 
way in them.  They'll just ignore w if it isn't relevant to them.  Your 
foo() code becomes something like this:

   x <- y <- z <- 0

   # here is my scope problem
   result <- list(x = x, y = y, z = z) # c == 0 case
   if (c==1) result <- bar1()
   if (c==2) result <- bar2()
   x <- result[["x"]]
   y <- result[["y"]]
   z <- result[["z"]]

Duncan Murdoch


> 
> 
> ----- Original Message -----
> From: "Duncan Murdoch" <murdoch.duncan using gmail.com>
> To: "Sebastien Bihorel" <sebastien.bihorel using cognigencorp.com>, r-help using r-project.org
> Sent: Tuesday, October 30, 2018 4:13:05 PM
> Subject: Re: [R] Question about function scope
> 
> On 30/10/2018 3:56 PM, Sebastien Bihorel wrote:
>> Hi,
>>
>>   From the R user manual, I have a basic understanding of the scope of function evaluation but have a harder time understanding how to mess with environments.
>>
>> My problem can be summarized by the code shown at the bottom:
>> - the foo function performs some steps including the assignment of default values to 3 objects: x, y, z
>> - at some point, I would like to call either the bar1 or bar2 function based upon the value of the c argument of the foo function. These functions assign different values to the x, y, z variables.
>> - then foo should move on and do other cool stuff
>>
>> Based upon default R scoping, the x, y, and z variables inside the bar1 and bar2 functions are not in the same environment as the x, y, and z variables created inside the foo function.
>>
>> Can I modify the scope of evaluation of bar1 and bar2 so that x, y, and z created inside the foo function are modified?
>>
>> PS:
>> - I know about "<<-" but, in my real code (which I cannot share, sorry), foo is already called within other functions and x, y, and z variables do not exist in the top-level environment and are not returned by foo. So "<<-" does not work (per manual: " Only when <<- has been used in a function that was returned as the value of another function will the special behavior described here occur. ")
> 
> I haven't looked up that quote, but it is likely describing a situation
> that isn't relevant to you.  For you, the important part is that bar1
> and bar2 must be created within foo.  They don't need to be returned
> from it.
> 
> So my edit below of your code should do what you want.
> 
> foo <- function(a=1, b=2, c=0){
> 
>     bar1 <- function(){
>       x <<- 1
>       y <<- 1
>       z <<- 1
>       cat(sprintf('bar1: x=%d, y=%d, z=%d\n', x, y, z))
>     }
> 
>     bar2 <- function(){
>       x <<- 2
>       y <<- 2
>       z <<- 2
>       cat(sprintf('bar2: x=%d, y=%d, z=%d\n', x, y, z))
>     }
> 
>     # some setup code
>     dummy <- a + b
>     x <- y <- z <- 0
> 
>     # here is my scope problem
>     if (c==1) bar1()
>     if (c==2) bar2()
> 
>     # some more code
>     cat(sprintf('foo: x=%d, y=%d, z=%d\n', x, y, z))
> 
> }
> 
> foo(c=0)
> foo(c=1)
> foo(c=2)
> 
> I get this output:
> 
>   > foo(c=0)
> foo: x=0, y=0, z=0
>   > foo(c=1)
> bar1: x=1, y=1, z=1
> foo: x=1, y=1, z=1
>   > foo(c=2)
> bar2: x=2, y=2, z=2
> foo: x=2, y=2, z=2
> 
> Duncan Murdoch
>




More information about the R-help mailing list