purrr quietly() in R: Trap Output, Warnings, Messages
purrr quietly() in R wraps a function so its printed output, warnings, and messages are captured as data instead of spilling to the console. The wrapped function returns a tidy four-element list you can inspect, store, or filter.
quietly(f) # wrap a function quietly(f)(x)$result # the return value quietly(f)(x)$output # captured print() output quietly(f)(x)$warnings # warnings as a character vector quietly(f)(x)$messages # messages as a character vector map(inputs, quietly(f)) # run quietly across a list map(runs, "warnings") # pluck warnings from each run
Need explanation? Read on for examples and pitfalls.
What quietly() does
quietly() turns side-effects into data. Most R functions emit warnings and messages straight to the console. Those signals do not stop execution, which makes them easy to miss in a long script and impossible to test programmatically. quietly() solves this by capturing those signals where you can actually work with them.
It belongs to a small group of purrr functions called adverbs. An adverb takes one function and returns a modified version of it, leaving the original untouched. safely() and possibly() are adverbs for errors; quietly() is the adverb for the quieter side-effects. The wrapped function still does exactly what the original did, accepts the same arguments, and produces the same result. The only change is what happens to the noise.
When you call the wrapped function, its return value is no longer a bare value. Instead you get a named list with four parts: result holds the original return value, output holds anything sent to print() or cat(), warnings holds a character vector of warning text, and messages holds a character vector of message text. Nothing reaches the console, so you can examine each channel on its own terms.
res$warnings rather than the console, you can count it, log it, store it, or branch on it. That is the entire purpose of quietly(): it makes the noisy parts of a function as queryable as its return value.quietly() syntax and arguments
quietly() takes a single argument. You pass the function you want to silence, and you get back a new function with the same call signature as the original.
The only argument is .f, the function to wrap. It accepts a plain function object, an anonymous function written inline, or a string naming a function such as "sqrt". The returned function forwards every argument straight through to .f, so quiet_sqrt(-4) behaves like sqrt(-4) in every way except that the warning is trapped rather than printed.
The shape of the returned list never changes. Even when a function emits no warnings at all, the warnings slot still exists as a zero-length character vector. Here sqrt(-4) produces NaN plus a warning, and both are reachable by name:
Four worked examples
Capture everything from a noisy function
Start with a function that emits all three side-effects. This example prints, messages, and warns in one call, so every slot of the returned list gets filled. Seeing all four channels populated at once makes the structure concrete.
The console stays completely silent. result$result holds 40, the print call is stored as a string in output, and the message and warning sit in their own slots. Notice the message keeps its trailing newline, because message() adds one before sending text.
Collect warnings from coercion
Real warnings are worth capturing too. Converting bad strings with as.numeric() triggers the familiar "NAs introduced by coercion" warning. With quietly(), that warning becomes a value you can keep alongside the partially converted result.
Run quietly across a list with map()
quietly() shines when paired with map(). Wrap the function once, then map it over many inputs to get one capture list per element. This is the pattern that makes quietly() worth learning.
runs is a list of three capture lists. The string shortcut map(runs, "result") plucks the result slot out of each one, so you get the values without the surrounding noise.
Flag which inputs produced warnings
Now the captured data earns its keep. Because warnings are stored as vectors, you can test their length to find out exactly which inputs were problematic, then act on that.
Two of the three inputs raised a warning, and you found out without reading a single console line. In a batch job, this turns a silent data-quality problem into a clear audit trail.
quietly() vs safely() vs possibly()
quietly() is the side-effects adverb; safely() and possibly() are the error adverbs. They share the same wrapping idea but solve different problems, so picking the right one matters.
| Adverb | Captures | Returns | Use when |
|---|---|---|---|
quietly() |
output, warnings, messages | list of result, output, warnings, messages |
you want to inspect side-effects |
safely() |
errors | list of result and error |
a call might throw an error |
possibly() |
errors | the result, or your otherwise default |
you want a fallback on error |
The decision rule is simple. If your concern is a function that stops execution, reach for safely() or possibly(). If your concern is a function that runs fine but complains, reach for quietly(). When a call can do both, nest the adverbs: safely(quietly(f)) captures the warnings inside the success branch and still traps any error in its own slot.
stop() still aborts your code. quietly() only intercepts output, warnings, and messages, never errors. If a call can both warn and fail, combine quietly() with safely() rather than expecting quietly() to handle everything.Common pitfalls
Three mistakes account for most quietly() confusion. Each has a quick fix once you can see it.
Forgetting the result is nested. The wrapped function never returns a bare value. It always returns the four-element list, so using the call directly in arithmetic silently breaks downstream code:
Always reach into $result when you want the value the original function would have returned.
Expecting errors to be trapped. quietly() ignores stop() entirely, so a wrapped function that errors still aborts. Wrap with safely() when failure is possible:
Confusing output with result. The output slot holds captured console text from print() or cat(), while result holds the actual return value. They are different channels and usually hold different things. A function that prints a table but returns a data frame will put the printed text in output and the data frame in result.
Try it yourself
Try it: Wrap log with quietly() and call the wrapped function on -1. log(-1) returns NaN and raises a warning. Pull the warning text out of the capture list, storing the full list in ex_result.
Click to reveal solution
Explanation: quietly(log) returns a wrapped function. Calling it on -1 runs log(-1), which yields NaN with a warning. The warning text is captured in the warnings slot instead of printing to the console.
Related purrr functions
quietly() belongs to the purrr adverb family. These functions modify other functions rather than mapping over data:
safely()wraps a function to capture errors as aresultanderrorpair.possibly()returns a default value when a function errors.insistently()retries a function that fails intermittently.compose()chains several functions into one combined function.negate()flips a predicate function'sTRUEandFALSEresults.
For the full argument reference, see the purrr quietly() documentation on the tidyverse site.
FAQ
Does quietly() catch errors in R?
No. quietly() only captures printed output, warnings, and messages. If the wrapped function calls stop(), the error still propagates and halts your code. To trap errors, use safely(), which returns a list with result and error elements, or possibly(), which substitutes a default value. You can nest the adverbs, such as safely(quietly(f)), to capture both side-effects and errors from a single call.
What is the difference between quietly() and suppressWarnings()?
suppressWarnings() discards warnings completely, leaving no record that anything happened. quietly() keeps them: the warnings are stored in the returned list so you can count, log, or test them later. Use suppressWarnings() when a warning is genuinely noise you never need again. Use quietly() when you want the warning out of the console but still need it available as data.
What does quietly() return?
The wrapped function returns a named list with four elements. result is the original return value of the function. output is a character string of anything sent to print() or cat(). warnings is a character vector of warning messages, and messages is a character vector of message text. Empty channels return zero-length vectors, so length(res$warnings) is a reliable test for whether any warning fired.
Can I use quietly() with map()?
Yes, and it is the most common pattern. Pass quietly(f) as the function argument to map() to get one capture list per input. You can then call map(runs, "result") to extract just the values, or map(runs, "warnings") to gather every warning across the run. This makes quietly() ideal for batch processing where you want to audit side-effects after the work is done.