coding style: use of vars vis-a-vis functions

recommendations

#1

Hello-

I'm looking for an opinionated view/ best practice rec on a coding pattern I run into.

Suppose we're writing functions for an app or a package: What assumptions about existing global environment variables can be made, and how should this be robustly anticipated by functions?

For instance, if we have a list whose contents will be used by various functions, is one of the following styles preferable to another?

# Some useful list, maybe stored locally a hidden file R object, which
# would be loaded via loadRDS, defined here as a list for the reprex
.credentials <- list(logon = "lisa", token = 1234)

# Option 1: assume .creds exists, but throw an error if not

option_1 <- function(x) {
  stopifnot(exists(".credentials"))
 .credentials$token + x
}
option_1(10)
#> [1] 1244

# Option 2: Pass an assigned value
option_2 <- function(x, token = .credentials$token) {
  token + x
}

option_2(20)
#> [1] 1254

# Option 3: Pass an assigned value explicitly every time
option_2(30, token = .credentials$token)
#> [1] 1264

Created on 2018-10-14 by the reprex package (v0.2.1)

Of course, I'm thinking about more complex situations and where this might create problems down the line, and the tradeoffs between verbosity, readability, and brittleness.

I'd be grateful for any advice or pointers to any related posts, blogs etc.

Thank you.


#2

Personally, I would use option 3 (or option 2 since there is practically no difference between them) pretty much always. There is very little upside of having hidden state in your program with large number of downsides.

In terms of where to look for more information on this, I would say talks by Rich Hickey (author of Clojure) are a good start, as well as entire idea of functional programming paradigm where pure functions (no side effects and no reliance on global state) are the main building block.

So, to answer your main question explicitly:

What assumptions about existing global environment variables can be made, and how should this be robustly anticipated by functions?

Main assumption is that global environment doesn't hold any data you can read from or write to. All inputs and outputs of functions are explicit.