knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
A regular R variable lives in whatever environment created it. Pass it into a function, and the function only sees a copy; change that copy inside the function, and the original outside is untouched. Assign inside a for loop or an lapply() call and, again, only the local copy changes. This is normal, sensible R scoping — but it also means that sharing genuinely mutable state across scopes (functions, loops, apply-family calls, nested environments) usually forces you to reach for <<-, explicit environments, or reference-class objects, all of which add ceremony to what is conceptually a simple idea: "I want one variable that stays the same object no matter where I touch it from."
newSuperVar() creates exactly that: a super variable — a named object that is attached to the search path so it is visible and mutable from any scope in the current R session, with optional protections layered on top: type/class enforcement, an explicit lock, and a cap on how many times its value may be changed.
# Task: create a super variable to store a dataset that should not be altered newSuperVar(mtdf, value = austres) # create a super variable named mtdf head(mtdf) # view it like any other object mtdf.class # view the stored class of the variable, e.g. "ts" # once created, any new value assigned to mtdf via mtdf.set() MUST have this same class
newSuperVar(variable, value = 0L, lock = FALSE, editn = NULL) takes the (unquoted) name you want the super variable to have, the initial value, whether it should be locked, and editn, an optional cap on the number of times it may later be changed.
Two things happen the moment you call it:
mtdf becomes available by that name from anywhere in your R session — not just the environment where you called newSuperVar().mtdf., are attached alongside it (mtdf.set(), mtdf.rm(), mtdf.contains(), mtdf.round(), mtdf.signif(), mtdf.class, plus mtdf.push()/mtdf.pop(), and — for data frames specifically — mtdf.head()/mtdf.tail()).Trying to declare a super variable under a name that already exists produces an error telling you to remove the existing one first (see .rm() below) rather than silently overwriting it.
.set() — updating the valueOrdinary reassignment (mtdf <- something_else) will not change the super variable itself; it just creates a local variable that happens to shadow the name in your current scope. To actually update the stored value, use .set():
newSuperVar(edtvec, value = number(5)) edtvec # view content of the vector # edtvec.set(letters) # ERROR: Cannot set to a value with a different class # # than the original value (numeric here, letters is character) edtvec.set(number(20)) # set to new numbers edtvec # view updated output
Because .set() is a function, not a <- assignment, it works identically no matter which scope it is called from:
for (pu in 1:8) { print(edtvec) # view output within the loop edtvec.set(number(pu)) # set to new numbers within the for loop } lc <- lapply(1:8, function(pu) { print(edtvec) # view output within the loop edtvec.set(number(pu)) # set to new numbers within the lapply loop }) # compare that to an ordinary local variable, which lapply() cannot mutate: bim <- 198 lc <- lapply(1:8, function(j) { print(bim) bim <- j # only changes the local copy inside this call; outer bim is untouched })
Every call to .set() checks the class of the new value against the class the super variable was created with, and refuses the update (with an informative error) if they don't match. This is what makes super variables safer to share across a large script than a plain global variable: a typo that assigns the wrong kind of value is caught immediately instead of silently corrupting downstream logic.
.round() and .signif() — numeric rounding in placeFor numeric or data-frame-of-numeric super variables, .round(digits) and .signif(digits) update the stored value in place, rounding to the given number of decimal places or significant digits respectively:
newSuperVar(mtdf3, value = beaver1, lock = TRUE) head(mtdf3) # view original values mtdf3.round(1) # round to 1 decimal place head(mtdf3) # view rounded values mtdf3.signif(2) # round to 2 significant digits head(mtdf3) # view again
.contains() — searching the value.contains() checks whether a pattern or value is present anywhere in the super variable, using the same matching options as grep() (ignore.case, perl, fixed, useBytes, invert). For vectors it searches the vector directly; for a data frame, matrix, or array it searches within a specific column, named via the df.col argument:
# Task: create and search a data frame
newSuperVar(lon2, value = mtcars) # declares lon2
lon2 # view content of lon2
lon2.contains("21.0", df.col = "mpg") # search the mpg column
# for the character "21.0"
lon2.contains(21.0, df.col = "mpg") # search the mpg column
# for the numeric value 21.0
# remove lon2 as a super variable once done with it
exists("lon2") # TRUE, before removal
lon2.rm()
exists("lon2") # FALSE, after removal
# Task: create and search a vector newSuperVar(lon3, value = number(10, seed = 12)) # declares lon3 lon3 # view content of lon3 lon3.contains(72) # TRUE/FALSE - does the vector contain 72? lon3.contains(72, fixed = TRUE) # same search, using fixed (non-regex) matching lon3.rm() # remove lon3 as a super variable
.push() and .pop() — building a new value from the current one.push(add, .df = c("row", "col")) and .pop(n = 10, .df = c("row", "col")) compute what the super variable's vector, list, or data frame would look like with an element added or removed (for data frames, a row or column, controlled by .df), and return that result — they do not update the stored value on their own. To actually persist the change, feed the result back into .set():
newSuperVar(queue, value = c(1, 2, 3)) queue.push(4) # returns c(1, 2, 3, 4), queue itself is still c(1, 2, 3) queue.set(queue.push(4)) # now queue is actually updated to c(1, 2, 3, 4) queue.set(queue.pop(1)) # drop the last element and persist the change
.head() and .tail() — previewing data framesWhen a super variable's initial value is a data frame, two extra helpers are attached automatically: .head(n = 10) and .tail(n = 10), printing the first or last n rows. They are not created for non-data-frame super variables.
.rm() — removing a super variable.rm() detaches the super variable and all of its companion functions, freeing up the name for reuse (including re-declaring it with newSuperVar() again):
exists("lon3") # TRUE
lon3.rm()
exists("lon3") # FALSE
Passing lock = TRUE at creation time adds an extra layer of protection around the stored binding, intended for values you want to guard more strictly against being changed by anything other than the super variable's own methods:
newSuperVar(mtdf3, value = beaver1, lock = TRUE)
Locked or not, updates always go through .set(), .round(), or .signif() — those methods know how to handle the lock internally, so day-to-day usage looks identical whether lock is TRUE or FALSE. Use lock = TRUE for values you want to flag as intentionally protected, such as reference datasets or configuration you don't want casually reassigned elsewhere in a long script.
editn caps the number of successful .set() calls a super variable will accept:
# Task: create a super variable that can only be edited 3 times newSuperVar(man1, value = number(5), editn = 3) man1 # view value man1.set(number(10)) # change value - 1st edit, succeeds man1 man1.set(number(2)) # change value - 2nd edit, succeeds man1 man1.set(number(1)) # change value - 3rd edit, succeeds man1 man1.set(number(5)) # 4th attempted edit - does NOT change the value, man1 # because the maximum of 3 edits has been used up
editn = NULL (the default) allows unlimited edits.editn = 0 prevents editing entirely — in this case, .set() is not even attached to the super variable, so there is no way to change its value short of removing it with .rm() and creating it again..set() calls silently stop taking effect.Super variables are a good fit when you want:
lapply()/sapply() calls, or nested functions, without threading it through as an explicit argument and return value everywhere..set()), and optionally only a limited number of times (editn).Because a super variable's companion functions are derived from its name by appending suffixes like .set, .rm, and .class, avoid using names that already end in a period followed by one of those words for something else in the same session, and avoid creating two super variables whose derived helper names would collide. If you need a fresh super variable under a name you've already used, call <name>.rm() first — newSuperVar() deliberately refuses to redeclare an existing super variable in place.
vignette("quickcode_r_introduction") for a broader tour of the packagevignette("not_functions_data_validation_r") for the not.* family used throughout quickcode's own internals, including inside newSuperVar()Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.