Harrison Jones
Harrison Jones

Reputation: 2506

How to reference an object between functions that are defined in the global environment

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()

Upvotes: 0

Views: 93

Answers (2)

MrFlick
MrFlick

Reputation: 206197

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

Upvotes: 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.

Upvotes: 1

Related Questions