Site icon R-bloggers

cRafty tRicks – No more typing brackets!

[This article was first published on Jozef's Rblog, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

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 qq

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

Oops…I Did It Again

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 it options("error") = NULL? Or maybe even options(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:

  1. 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
  2. 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:

Tends to be more useful in the terminal

References

  1. Rprofile chapter of Efficient R programming
  2. Documentation on print
  3. 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!

Did you find the article helpful or interesting? Help others find it by sharing

To leave a comment for the author, please follow the link and comment on their blog: Jozef's Rblog.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.