Something for the global namespace:

global = 42

And a function accessing it:

fun = function (globl) {
    message('Called as: ', deparse(match.call()))
    global * 2 # Note the typo
}

Let’s call it:

fun(1)
Called as: fun(globl = 1)
[1] 84

Ouch, wrong object accessed!

No worries, set the function environment to something that doesn’t inherit the globalenv():

environment(fun) = emptyenv()
try(fun(1))
Error in fun(1) : could not find function "{"

Shoot! The problem: virtually everything — including `{` and `*` — is defined inside some package (such as base). emptyenv() by default does not inherit any objects; it is empty. We can’t even qualify object access via the pkg::obj syntax, because `::` is also defined in baseenv().

One solution would be to use baseenv() instead of emptyenv():

fun2 = function () {
    # Rather than generating an error, inspect the parent environment.
    parent = parent.env(environment())
    env_name = environmentName(parent)
    env_name = if (env_name == '')
        'Unnamed parent environment'
    else
        paste('Parent environment', dQuote(env_name))
    env_size = length(ls(envir = parent))
    sprintf('%s has %g objects', env_name, env_size)
}
    
environment(fun2) = baseenv()
fun2()
[1] "Parent environment “base” has 1215 objects"

That’s better. However, using baseenv() isn’t actually a good idea either because base is, well, base. And we don’t really have any business mucking around in it. Instead, we should create our own environment:

myenv = new.env(parent = baseenv())
environment(fun2) = myenv
fun2()
[1] "Unnamed parent environment has 0 objects"

There. This is what we want. We can make two more adjustments:

  1. If our environment inherits from baseenv(), we can only use objects defined there, and not others, defined in attached packages. This may or may not be what we want (we can still access other packages by explicitly qualifying them: pkg::obj).

    Alternatively, we can use a different parent environment, e.g. the parent environment of the global environment; that way, our function will have access to all attached packages, but not to the global environment itself.

  2. When defining a bunch of functions this way, we can use local or eval to define them directly inside our cleanroom environment.

Regarding the first point, we can simply write:

myenv = new.env(parent = parent.env(globalenv()))
environment(fun2) = myenv
fun2()
[1] "Unnamed parent environment has 0 objects"

The difference: we now have access to attached packages. E.g.:

fun3 = function () {
    plot(speed ~ dist, cars)
}
environment(fun3) = myenv
fun3()

Here plot comes from package:graphics, and cars comes from package:datasets.

Regarding the second point, we can either use local:

local({
    fun4 = function () 'hello'
    fun5 = function (x, y) x + y
    # … etc.
}, envir = myenv)
myenv$fun4()
[1] "hello"

… or we can put the functions into a separate file and use source with appropriate parameters:

source(file = 'mymodule.r', local = myenv)

But once we reach that point, we might as well use the ‹modules› package from GH:klmr/modules:

mod = modules::import('mymodule')
mod$fun4()
LS0tCnRpdGxlOiAiSXNvbGF0aW5nIGZ1bmN0aW9ucyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKYGBge3IgZWNobz1GQUxTRX0KIyBUbyBtYWtlIGtuaXRyIHdyaXRlIHRoZSBlcnJvciB0byB0aGUgZG9jdW1lbnQgcmF0aGVyIHRoYW4gdGhlIGxvZy4KYm9keSh0cnkpW1syXV1bWzNdXVtbM11dW1s2XV1bWzNdXVtbMl1dID0gcXVvdGUobWVzc2FnZShtc2cpKQpgYGAKClNvbWV0aGluZyBmb3IgdGhlIGdsb2JhbCBuYW1lc3BhY2U6CgpgYGB7cn0KZ2xvYmFsID0gNDIKYGBgCgpBbmQgYSBmdW5jdGlvbiBhY2Nlc3NpbmcgaXQ6CgpgYGB7cn0KZnVuID0gZnVuY3Rpb24gKGdsb2JsKSB7CiAgICBtZXNzYWdlKCdDYWxsZWQgYXM6ICcsIGRlcGFyc2UobWF0Y2guY2FsbCgpKSkKICAgIGdsb2JhbCAqIDIgIyBOb3RlIHRoZSB0eXBvCn0KYGBgCgpMZXTigJlzIGNhbGwgaXQ6CgpgYGB7cn0KZnVuKDEpCmBgYAoKT3VjaCwgd3Jvbmcgb2JqZWN0IGFjY2Vzc2VkIQoKTm8gd29ycmllcywgc2V0IHRoZSBmdW5jdGlvbiBlbnZpcm9ubWVudCB0byBzb21ldGhpbmcgdGhhdCBkb2VzbuKAmXQgaW5oZXJpdCB0aGUgYGdsb2JhbGVudigpYDoKCmBgYHtyfQplbnZpcm9ubWVudChmdW4pID0gZW1wdHllbnYoKQoKdHJ5KGZ1bigxKSkKYGBgCgpTaG9vdCEgVGhlIHByb2JsZW06IHZpcnR1YWxseSBldmVyeXRoaW5nIOKAlCBpbmNsdWRpbmcgYGAgYHtgIGBgIGFuZCBgYCBgKmAgYGAg4oCUIGlzIGRlZmluZWQgaW5zaWRlIHNvbWUgcGFja2FnZSAoc3VjaCBhcyBgYmFzZWApLiBgZW1wdHllbnYoKWAgYnkgZGVmYXVsdCBkb2VzIG5vdCBpbmhlcml0ICphbnkqIG9iamVjdHM7IGl0IGlzIGVtcHR5LiBXZSBjYW7igJl0IGV2ZW4gcXVhbGlmeSBvYmplY3QgYWNjZXNzIHZpYSB0aGUgYHBrZzo6b2JqYCBzeW50YXgsIGJlY2F1c2UgYGAgYDo6YCBgYCBpcyBhbHNvIGRlZmluZWQgaW4gYGJhc2VlbnYoKWAuCgpPbmUgc29sdXRpb24gd291bGQgYmUgdG8gdXNlIGBiYXNlZW52KClgIGluc3RlYWQgb2YgYGVtcHR5ZW52KClgOgoKYGBge3J9CmZ1bjIgPSBmdW5jdGlvbiAoKSB7CiAgICAjIFJhdGhlciB0aGFuIGdlbmVyYXRpbmcgYW4gZXJyb3IsIGluc3BlY3QgdGhlIHBhcmVudCBlbnZpcm9ubWVudC4KICAgIHBhcmVudCA9IHBhcmVudC5lbnYoZW52aXJvbm1lbnQoKSkKICAgIGVudl9uYW1lID0gZW52aXJvbm1lbnROYW1lKHBhcmVudCkKICAgIGVudl9uYW1lID0gaWYgKGVudl9uYW1lID09ICcnKQogICAgICAgICdVbm5hbWVkIHBhcmVudCBlbnZpcm9ubWVudCcKICAgIGVsc2UKICAgICAgICBwYXN0ZSgnUGFyZW50IGVudmlyb25tZW50JywgZFF1b3RlKGVudl9uYW1lKSkKICAgIGVudl9zaXplID0gbGVuZ3RoKGxzKGVudmlyID0gcGFyZW50KSkKICAgIHNwcmludGYoJyVzIGhhcyAlZyBvYmplY3RzJywgZW52X25hbWUsIGVudl9zaXplKQp9CiAgICAKZW52aXJvbm1lbnQoZnVuMikgPSBiYXNlZW52KCkKCmZ1bjIoKQpgYGAKClRoYXTigJlzIGJldHRlci4gSG93ZXZlciwgdXNpbmcgYGJhc2VlbnYoKWAgaXNu4oCZdCBhY3R1YWxseSBhIGdvb2QgaWRlYSBlaXRoZXIgYmVjYXVzZSBgYmFzZWAgaXMsIHdlbGwsIGBiYXNlYC4gQW5kIHdlIGRvbuKAmXQgcmVhbGx5IGhhdmUgYW55IGJ1c2luZXNzIG11Y2tpbmcgYXJvdW5kIGluIGl0LiBJbnN0ZWFkLCB3ZSBzaG91bGQgY3JlYXRlICpvdXIgb3duKiBlbnZpcm9ubWVudDogCgpgYGB7cn0KbXllbnYgPSBuZXcuZW52KHBhcmVudCA9IGJhc2VlbnYoKSkKZW52aXJvbm1lbnQoZnVuMikgPSBteWVudgpmdW4yKCkKYGBgCgpUaGVyZS4gVGhpcyBpcyB3aGF0IHdlIHdhbnQuIFdlIGNhbiBtYWtlIHR3byBtb3JlIGFkanVzdG1lbnRzOgoKMS4gSWYgb3VyIGVudmlyb25tZW50IGluaGVyaXRzIGZyb20gYGJhc2VlbnYoKWAsIHdlIGNhbiBvbmx5IHVzZSBvYmplY3RzIGRlZmluZWQgdGhlcmUsIGFuZCBub3Qgb3RoZXJzLCBkZWZpbmVkIGluIGF0dGFjaGVkIHBhY2thZ2VzLiBUaGlzIG1heSBvciBtYXkgbm90IGJlIHdoYXQgd2Ugd2FudCAod2UgY2FuIHN0aWxsIGFjY2VzcyBvdGhlciBwYWNrYWdlcyBieSBleHBsaWNpdGx5IHF1YWxpZnlpbmcgdGhlbTogYHBrZzo6b2JqYCkuCgogICAgQWx0ZXJuYXRpdmVseSwgd2UgY2FuIHVzZSBhIGRpZmZlcmVudCBgcGFyZW50YCBlbnZpcm9ubWVudCwgZS5nLiB0aGUgcGFyZW50IGVudmlyb25tZW50IG9mIHRoZSBnbG9iYWwgZW52aXJvbm1lbnQ7IHRoYXQgd2F5LCBvdXIgZnVuY3Rpb24gd2lsbCBoYXZlIGFjY2VzcyB0byBhbGwgYXR0YWNoZWQgcGFja2FnZXMsIGJ1dCBub3QgdG8gdGhlIGdsb2JhbCBlbnZpcm9ubWVudCBpdHNlbGYuCgoyLiBXaGVuIGRlZmluaW5nIGEgYnVuY2ggb2YgZnVuY3Rpb25zIHRoaXMgd2F5LCB3ZSBjYW4gdXNlIGBsb2NhbGAgb3IgYGV2YWxgIHRvIGRlZmluZSB0aGVtIGRpcmVjdGx5IGluc2lkZSBvdXIgY2xlYW5yb29tIGVudmlyb25tZW50LgoKUmVnYXJkaW5nIHRoZSBmaXJzdCBwb2ludCwgd2UgY2FuIHNpbXBseSB3cml0ZToKCmBgYHtyfQpteWVudiA9IG5ldy5lbnYocGFyZW50ID0gcGFyZW50LmVudihnbG9iYWxlbnYoKSkpCmVudmlyb25tZW50KGZ1bjIpID0gbXllbnYKZnVuMigpCmBgYAoKVGhlIGRpZmZlcmVuY2U6IHdlIG5vdyBoYXZlIGFjY2VzcyB0byBhdHRhY2hlZCBwYWNrYWdlcy4gRS5nLjoKCmBgYHtyfQpmdW4zID0gZnVuY3Rpb24gKCkgewogICAgcGxvdChzcGVlZCB+IGRpc3QsIGNhcnMpCn0KCmVudmlyb25tZW50KGZ1bjMpID0gbXllbnYKZnVuMygpCmBgYAoKSGVyZSBgcGxvdGAgY29tZXMgZnJvbSBgcGFja2FnZTpncmFwaGljc2AsIGFuZCBgY2Fyc2AgY29tZXMgZnJvbSBgcGFja2FnZTpkYXRhc2V0c2AuCgpSZWdhcmRpbmcgdGhlIHNlY29uZCBwb2ludCwgd2UgY2FuIGVpdGhlciB1c2UgYGxvY2FsYDoKCmBgYHtyfQpsb2NhbCh7CiAgICBmdW40ID0gZnVuY3Rpb24gKCkgJ2hlbGxvJwogICAgZnVuNSA9IGZ1bmN0aW9uICh4LCB5KSB4ICsgeQogICAgIyDigKYgZXRjLgp9LCBlbnZpciA9IG15ZW52KQoKbXllbnYkZnVuNCgpCmBgYAoK4oCmIG9yIHdlIGNhbiBwdXQgdGhlIGZ1bmN0aW9ucyBpbnRvIGEgc2VwYXJhdGUgZmlsZSBhbmQgdXNlIGBzb3VyY2VgIHdpdGggYXBwcm9wcmlhdGUgcGFyYW1ldGVyczoKCmBgYHtyIGV2YWw9RkFMU0V9CnNvdXJjZShmaWxlID0gJ215bW9kdWxlLnInLCBsb2NhbCA9IG15ZW52KQpgYGAKCkJ1dCBvbmNlIHdlIHJlYWNoIHRoYXQgcG9pbnQsIHdlIG1pZ2h0IGFzIHdlbGwgdXNlIHRoZSDigLltb2R1bGVz4oC6IHBhY2thZ2UgZnJvbSBbR0g6a2xtci9tb2R1bGVzXShodHRwczovL2dpdGh1Yi5jb20va2xtci9tb2R1bGVzKToKCmBgYHtyIGV2YWw9RkFMU0V9Cm1vZCA9IG1vZHVsZXM6OmltcG9ydCgnbXltb2R1bGUnKQptb2QkZnVuNCgpCmBgYAo=