minitrap

minitrap provides two purrr-style function adverbs, mtrap_safely() and mtrap_quietly(). A function adverb takes a function and returns a new function with modified behaviour – here, one that traps errors or console output instead of letting them propagate or print. See the README for the full API reference and scope notes; this page walks through both in action, and how they compose together.

Functions

  • mtrap_safely(.f) – wrap .f so that a call which would normally throw an error instead returns list(result = ..., error = ...), with exactly one of the two NULL.
  • mtrap_quietly(.f) – wrap .f so that printed output, warnings, and messages are captured instead of shown, returning list(result = ..., output = ..., warnings = ..., messages = ...).

minitrap has no internal helpers – both functions are short enough to be self-contained.

mtrap_safely()

Ordinarily, a function that errors stops execution and requires you to wrap every call site in tryCatch() if you want to keep going. mtrap_safely() inverts that: it takes a function and returns a new one that never throws, instead capturing the error as data. Calling mtrap_safely(log) on a non-numeric input, for example, doesn’t let the resulting error reach the console – it comes back as a list with result = NULL and error set to the condition object that would have been signalled:

safe_log <- mtrap_safely(log)
safe_log("a")
$result
NULL

$error
<simpleError in .f(...): non-numeric argument to mathematical function>

A successful call returns the result with error set to NULL instead, so the two fields are always mutually exclusive – checking is.null(out$error) tells you unambiguously whether the call succeeded:

safe_log(10)
$result
[1] 2.302585

$error
NULL

This is particularly useful when mapping a function over many inputs and some of them are expected to fail – rather than the whole iteration aborting on the first error, each element’s outcome (success or failure) is captured individually. Combining mtrap_safely() with minimap’s mmap_map() gives exactly that pattern (this page sources minimap.R too, purely to demonstrate the two minis working together – minitrap itself has no dependency on minimap):

results <- mmap_map(list(4, "a", 9), mtrap_safely(sqrt))
mmap_map_lgl(results, function(r) is.null(r$error))
[1]  TRUE FALSE  TRUE

mtrap_quietly()

Where mtrap_safely() is about errors, mtrap_quietly() is about everything a function might print along the way: cat()/print() output, warning()s, and message()s. Rather than letting any of that reach the console, mtrap_quietly() collects it all and hands it back as part of the return value:

quiet_fn <- mtrap_quietly(function() {
  message("starting")
  warning("using a default")
  42
})
quiet_fn()
$result
[1] 42

$output
[1] ""

$warnings
[1] "using a default"

$messages
[1] "starting\n"

Note the shape of the result: result holds the ordinary return value (42), warnings and messages are character vectors with one entry per condition raised (in the order they occurred), and output is whatever was printed via cat()/print(), collapsed into a single string – empty here, since this function didn’t print anything directly:

loud_fn <- mtrap_quietly(function() {
  cat("computing...\n")
  99
})
loud_fn()
$result
[1] 99

$output
[1] "computing..."

$warnings
character(0)

$messages
character(0)

Crucially, mtrap_quietly() does not catch errors – a function that errors still aborts the call, same as if it were called directly. Compose it with mtrap_safely() when you want both output-trapping and error-trapping at once:

mtrap_quietly(function() stop("boom"))()
Error in `.f()`:
! boom
robust_fn <- mtrap_safely(mtrap_quietly(function() stop("boom")))
robust_fn()
$result
NULL

$error
<simpleError in .f(...): boom>

Note the order of composition: mtrap_quietly() wraps the original function first, and mtrap_safely() wraps the result, so that the error thrown inside the quietly-wrapped call is the thing mtrap_safely() catches. Composing the other way around – mtrap_quietly(mtrap_safely(.f)) – would instead capture a function that never errors in the first place (since mtrap_safely() already turned errors into ordinary return values), so there would be nothing left for the outer mtrap_quietly() to trap on that front.