Calling functions in R usually involves typing brackets. And since many of our actions in R involve calling a function, we will have to type a lot of brackets working with R. Often it would make our life a lot easier if we could omit the need to type brackets where convenient. We will do exactly that today.
Work in R faster with custom bracketless commands
A good starting example is, well, quitting R altogether. Usually, one may do:
quit()
Which will in turn likely get you and extra question regarding saving a workspace image. So you then finally type n
and are done with it. If you want to be a bit faster, you may do:
q("no")
Better, but still an awful lot of typing just to quit R
, especially when working in a terminal-like environment with multiple sessions.
Let us be a bit craftier and make
R
quit just by typing
To make a bracketless command, we will (mis)use the fact that typing an object name into R console and pressing enter will often invoke a print method specific for the class of that object.
All we have to do to create our very first bracketless command is to create a custom print method for a funky class made for this single purpose. Then we make an object of that class and type its name to the console:
qq <- structure("no", class = "quitter")
print.quitter <- function(quitter) base::quit("no")
# This will quit your session NOT saving a workspace image!
qq
Switching debugging modes with ease
Quitting R
quickly is more useful then it may sound when using multiple sessions in a terminal environment, but we can use the above approach to create different useful shortcuts making our life much easier.
One example I use very frequently is to change the error
option, which governs how R
behaves when encountering non-catastrophic errors such as those generated by stop
, etc.
- I find setting the option to
options(error = utils::recover)
very useful for debugging and at the same time very annoying when undesired. - Typing
options(error = NULL)
to change it back is however even more annoying. Or is itoptions("error") = NULL
? Or maybe evenoptions(error) = NULL
?
In comes the gg
shortcut:
gg <- structure(FALSE, class = "debuggerclass")
print.debuggerclass <- function(debugger) {
if (!identical(getOption("error"), as.call(list(utils::recover)))) {
options(error = recover)
message(" * debugging is now ON - option error set to recover")
} else {
options(error = NULL)
message(" * debugging is now OFF - option error set to NULL")
}
}
Now we switch between the options with ease:
# When in need of debugging
gg
## * debugging is now ON - option error set to recover
# The option is now set to recover
getOption("error")
## (function ()
## {
## if (.isMethodsDispatchOn()) {
## tState <- tracingState(FALSE)
## on.exit(tracingState(tState))
## }
## calls <- sys.calls()
## from <- 0L
## n <- length(calls)
## if (identical(sys.function(n), recover))
## n <- n - 1L
## for (i in rev(seq_len(n))) {
## calli <- calls[[i]]
## fname <- calli[[1L]]
## if (!is.na(match(deparse(fname)[1L], c("methods::.doTrace",
## ".doTrace")))) {
## from <- i - 1L
## break
## }
## }
## if (from == 0L)
## for (i in rev(seq_len(n))) {
## calli <- calls[[i]]
## fname <- calli[[1L]]
## if (!is.name(fname) || is.na(match(as.character(fname),
## c("recover", "stop", "Stop")))) {
## from <- i
## break
## }
## }
## if (from > 0L) {
## if (!interactive()) {
## try(dump.frames())
## cat(gettext("recover called non-interactively; frames dumped, use debugger() to view\n"))
## return(NULL)
## }
## else if (identical(getOption("show.error.messages"),
## FALSE))
## return(NULL)
## calls <- limitedLabels(calls[1L:from])
## repeat {
## which <- menu(calls, title = "\nEnter a frame number, or 0 to exit ")
## if (which)
## eval(substitute(browser(skipCalls = skip), list(skip = 7 -
## which)), envir = sys.frame(which))
## else break
## }
## }
## else cat(gettext("No suitable frames for recover()\n"))
## })()
# When done debugging
gg
## * debugging is now OFF - option error set to NULL
# The option is now back to NULL
getOption("error")
## NULL
Making it practical (and a bit less barbaric)
Defining all the shortcuts in the way shown above every time is both tedious and ugly, making a mess in our global environment. We can therefore decrease the tedium and ugliness by:
- Adding the definitions into our
.Rprofile
with a proper notice, which will run the definitions and make the shortcuts available every time we start R standardly - Enclosing the definitions into a separate environment attached to the search path, potentially with a command to detach it easily
Such an .Rprofile
can look similar to:
message("________________________________________")
message("| |")
message("| SOURCING CUSTOM .Rprofile |")
message("| |")
message("| * qq => quit('no') |")
message("| * gg => toggle error = recover/NULL |")
message("| * dd => detach this madness |")
message("|______________________________________|")
message("\n")
customCommands <- new.env()
assign("qq", structure("no", class = "quitterclass"), envir = customCommands)
assign("print.quitterclass", function(quitter) {
message(" * quitting, not saving workspace")
base::quit(quitter[1L])
}, envir = customCommands)
assign("gg", structure("", class = "debuggerclass"), envir = customCommands)
assign("print.debuggerclass", function(debugger) {
if (!identical(getOption("error"), as.call(list(utils::recover)))) {
options(error = recover)
message(" * debugging is now ON - option error set to recover")
} else {
options(error = NULL)
message(" * debugging is now OFF - option error set to NULL")
}
}, envir = customCommands)
assign("dd", structure("", class = "detacherclass"), envir = customCommands)
assign("print.detacherclass", function(detacher) {
detach(customCommands, unload = TRUE, force = TRUE)
})
attach(customCommands)
In terminal environments, shortcuts like this can be even more useful:
References
- Rprofile chapter of Efficient R programming
- Documentation on print
- Documentation on options to set and examine a variety of global options.
Today, September 1st 2018 the Constitution of the Slovak Republic celebrates its 26th anniversary. Happy Birthday!