0

I have created two functions below that are both defined in the global environment. You'll notice that foo() is called within bar() and they share the one input which is x. :

bar <- function() {
  x <- 2
  foo()
}

foo <- function(x) {
  x * 1000
}

x is not explicitly defined when foo() is called within bar(), which is causing an error with the following code:

bar()

My question is this: is there a way to define the environment where foo() tries to find x? From my research, foo() is looking for x in the global environment, but ideally it should be taking x from the environment of bar(), where x <- 2.

I know that the following would fix the problem, but this isn't a reasonable option in the code I'm developing. The key is getting foo() to reference the x value from the environment of bar():

bar <- function() {
  x <- 2
  foo(x = x)
}

foo <- function(x) {
  x * 1000
}

bar()
Harrison Jones
  • 2,256
  • 5
  • 27
  • 34
  • You may need to specify the envir – akrun Jul 06 '20 at 20:04
  • The enclosing environment for `foo` is the global environment, hence it will look for `x` in there. You can define `foo` inside `bar` – slava-kohut Jul 06 '20 at 20:05
  • @akrun how would I specify the environment? Would this be using eval()? – Harrison Jones Jul 06 '20 at 20:08
  • I am not sure about all the test cases for this example. But, if you do `foo <- function() { x* 1000 }` should work – akrun Jul 06 '20 at 20:12
  • Removing `x` as a function argument did not work for me. I get the error `object 'x' not found` – Harrison Jones Jul 06 '20 at 20:22
  • @slava-kohut Unfortunately that's not an option since I have multiple `bar()s`, so I would prefer not to define `foo()` individually in all the separate functions. – Harrison Jones Jul 06 '20 at 20:23
  • I don't understand why passing parameters "isn't a reasonable option" in your case. What makes it different from normal R code? Perhaps you should be passing around expressions rather than functions. – MrFlick Jul 06 '20 at 21:09

2 Answers2

1

This will give you something akin to what you want. The trick is to use a closure to define the function(/environment) you want, and then use the double left arrow assign to assign the value of x within bar. Double left arrow assign gives you the ability to write to a variable in the parent environment.

make_foo_bar_pair = function() {
  # Define the shared state...
  x <- 0

  # Make your bar function
  bar <- function() {
    x <<- 2 # Use the double left arrow assign to modify the shared state
    foo()
  }
  
  # Make your foo function
  foo <- function() {
    x * 1000 # Which references the shared state
  }
  
  # Return your linked functions...
  list(foo, bar)
}

# Make a pair of functions with shared state...
pair = make_foo_bar_pair()
foo = pair[[1]]
bar = pair[[2]]

# Call them
foo() # 0 (foo uses the initial value of x that it closed over)
bar() # 2000
foo() # 2000

Each foo/bar pair that you create will reference a different x, so calling make_foo_bar_pair repeatedly will give you fresh foo/bar functions sharing a distinct x.

If you really need to use the global environment you could do this...

# Make your bar function
bar <- function() {
  x <<- 2 # Use the double left arrow assign to modify the shared state
  foo()
}
  
# Make your foo function
foo <- function() {
  x * 1000 # Which references the shared state
}

...but doing things in the global environment like that feels like a code smell.

Restore the Data Dumps
  • 38,967
  • 12
  • 96
  • 122
1

It seems like you maybe shoudl be using expressions here for functions like f. Maybe

foo <- quote(x * 1000)
bar <- function() {
  x <- 2
  eval(foo)
}
bar()
#[1] 2000

Or if it does have to be a function

foo <- function() {
  x * 1000 
}

bar <- function() {
  x <- 2
  environment(foo)<-environment()
  foo()
}
bar()
#[1] 2000

Note that this doesn't permanently change foo, it just makes a local copy of foo in bar with a different enclosing environment.

The latter solution is much like my solution for the previous question: R specify function environment

MrFlick
  • 195,160
  • 17
  • 277
  • 295