727 lines
40 KiB
HTML
Raw Permalink Normal View History

2025-01-12 00:52:51 +08:00
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta http-equiv="X-UA-Compatible" content="IE=EDGE" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Changing and restoring state</title>
<script>// Pandoc 2.9 adds attributes on both header and div. We remove the former (to
// be compatible with the behavior of Pandoc < 2.8).
document.addEventListener('DOMContentLoaded', function(e) {
var hs = document.querySelectorAll("div.section[class*='level'] > :first-child");
var i, h, a;
for (i = 0; i < hs.length; i++) {
h = hs[i];
if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6
a = h.attributes;
while (a.length > 0) h.removeAttribute(a[0].name);
}
});
</script>
<style type="text/css">
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
</style>
<style type="text/css">
code {
white-space: pre;
}
.sourceCode {
overflow: visible;
}
</style>
<style type="text/css" data-origin="pandoc">
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { display: inline-block; text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; }
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; }
code span.at { color: #7d9029; }
code span.bn { color: #40a070; }
code span.bu { color: #008000; }
code span.cf { color: #007020; font-weight: bold; }
code span.ch { color: #4070a0; }
code span.cn { color: #880000; }
code span.co { color: #60a0b0; font-style: italic; }
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; }
code span.do { color: #ba2121; font-style: italic; }
code span.dt { color: #902000; }
code span.dv { color: #40a070; }
code span.er { color: #ff0000; font-weight: bold; }
code span.ex { }
code span.fl { color: #40a070; }
code span.fu { color: #06287e; }
code span.im { color: #008000; font-weight: bold; }
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; }
code span.kw { color: #007020; font-weight: bold; }
code span.op { color: #666666; }
code span.ot { color: #007020; }
code span.pp { color: #bc7a00; }
code span.sc { color: #4070a0; }
code span.ss { color: #bb6688; }
code span.st { color: #4070a0; }
code span.va { color: #19177c; }
code span.vs { color: #4070a0; }
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; }
</style>
<script>
// apply pandoc div.sourceCode style to pre.sourceCode instead
(function() {
var sheets = document.styleSheets;
for (var i = 0; i < sheets.length; i++) {
if (sheets[i].ownerNode.dataset["origin"] !== "pandoc") continue;
try { var rules = sheets[i].cssRules; } catch (e) { continue; }
var j = 0;
while (j < rules.length) {
var rule = rules[j];
// check if there is a div.sourceCode rule
if (rule.type !== rule.STYLE_RULE || rule.selectorText !== "div.sourceCode") {
j++;
continue;
}
var style = rule.style.cssText;
// check if color or background-color is set
if (rule.style.color === '' && rule.style.backgroundColor === '') {
j++;
continue;
}
// replace div.sourceCode by a pre.sourceCode rule
sheets[i].deleteRule(j);
sheets[i].insertRule('pre.sourceCode{' + style + '}', j);
}
}
})();
</script>
<style type="text/css">body {
background-color: #fff;
margin: 1em auto;
max-width: 700px;
overflow: visible;
padding-left: 2em;
padding-right: 2em;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.35;
}
#TOC {
clear: both;
margin: 0 0 10px 10px;
padding: 4px;
width: 400px;
border: 1px solid #CCCCCC;
border-radius: 5px;
background-color: #f6f6f6;
font-size: 13px;
line-height: 1.3;
}
#TOC .toctitle {
font-weight: bold;
font-size: 15px;
margin-left: 5px;
}
#TOC ul {
padding-left: 40px;
margin-left: -1.5em;
margin-top: 5px;
margin-bottom: 5px;
}
#TOC ul ul {
margin-left: -2em;
}
#TOC li {
line-height: 16px;
}
table {
margin: 1em auto;
border-width: 1px;
border-color: #DDDDDD;
border-style: outset;
border-collapse: collapse;
}
table th {
border-width: 2px;
padding: 5px;
border-style: inset;
}
table td {
border-width: 1px;
border-style: inset;
line-height: 18px;
padding: 5px 5px;
}
table, table th, table td {
border-left-style: none;
border-right-style: none;
}
table thead, table tr.even {
background-color: #f7f7f7;
}
p {
margin: 0.5em 0;
}
blockquote {
background-color: #f6f6f6;
padding: 0.25em 0.75em;
}
hr {
border-style: solid;
border: none;
border-top: 1px solid #777;
margin: 28px 0;
}
dl {
margin-left: 0;
}
dl dd {
margin-bottom: 13px;
margin-left: 13px;
}
dl dt {
font-weight: bold;
}
ul {
margin-top: 0;
}
ul li {
list-style: circle outside;
}
ul ul {
margin-bottom: 0;
}
pre, code {
background-color: #f7f7f7;
border-radius: 3px;
color: #333;
white-space: pre-wrap;
}
pre {
border-radius: 3px;
margin: 5px 0px 10px 0px;
padding: 10px;
}
pre:not([class]) {
background-color: #f7f7f7;
}
code {
font-family: Consolas, Monaco, 'Courier New', monospace;
font-size: 85%;
}
p > code, li > code {
padding: 2px 0px;
}
div.figure {
text-align: center;
}
img {
background-color: #FFFFFF;
padding: 2px;
border: 1px solid #DDDDDD;
border-radius: 3px;
border: 1px solid #CCCCCC;
margin: 0 5px;
}
h1 {
margin-top: 0;
font-size: 35px;
line-height: 40px;
}
h2 {
border-bottom: 4px solid #f7f7f7;
padding-top: 10px;
padding-bottom: 2px;
font-size: 145%;
}
h3 {
border-bottom: 2px solid #f7f7f7;
padding-top: 10px;
font-size: 120%;
}
h4 {
border-bottom: 1px solid #f7f7f7;
margin-left: 8px;
font-size: 105%;
}
h5, h6 {
border-bottom: 1px solid #ccc;
font-size: 105%;
}
a {
color: #0033dd;
text-decoration: none;
}
a:hover {
color: #6666ff; }
a:visited {
color: #800080; }
a:visited:hover {
color: #BB00BB; }
a[href^="http:"] {
text-decoration: underline; }
a[href^="https:"] {
text-decoration: underline; }
code > span.kw { color: #555; font-weight: bold; }
code > span.dt { color: #902000; }
code > span.dv { color: #40a070; }
code > span.bn { color: #d14; }
code > span.fl { color: #d14; }
code > span.ch { color: #d14; }
code > span.st { color: #d14; }
code > span.co { color: #888888; font-style: italic; }
code > span.ot { color: #007020; }
code > span.al { color: #ff0000; font-weight: bold; }
code > span.fu { color: #900; font-weight: bold; }
code > span.er { color: #a61717; background-color: #e3d2d2; }
</style>
</head>
<body>
<h1 class="title toc-ignore">Changing and restoring state</h1>
<div class="sourceCode" id="cb1"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb1-1"><a href="#cb1-1" tabindex="-1"></a><span class="fu">library</span>(withr)</span></code></pre></div>
<!-- This vignette uses a git-diff-friendly convention of ONE LINE PER SENTENCE. -->
<p>This article explains the type of problem withr solves and shows
typical patterns of usage. It also compares withrs functionality to the
<code>on.exit()</code> function from base R.</p>
<div id="its-dangerous-to-change-state" class="section level2">
<h2>Its dangerous to change state</h2>
<p>Whenever possible, it is desirable to write so-called
<strong>pure</strong> functions. The property we focus on here is that
the function should not change the surrounding R landscape, i.e. it
should not change things like the search path, global options, or the
working directory. If the behaviour of <em>other</em> functions differs
before and after running your function, youve modified the landscape.
Changing the landscape is bad because it makes code much harder to
understand.</p>
<p>Heres a <code>sloppy()</code> function that prints a number with a
specific number of significant digits, by adjusting Rs global “digits”
option.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb2-1"><a href="#cb2-1" tabindex="-1"></a>sloppy <span class="ot">&lt;-</span> <span class="cf">function</span>(x, sig_digits) {</span>
<span id="cb2-2"><a href="#cb2-2" tabindex="-1"></a> <span class="fu">options</span>(<span class="at">digits =</span> sig_digits)</span>
<span id="cb2-3"><a href="#cb2-3" tabindex="-1"></a> <span class="fu">print</span>(x)</span>
<span id="cb2-4"><a href="#cb2-4" tabindex="-1"></a>}</span>
<span id="cb2-5"><a href="#cb2-5" tabindex="-1"></a></span>
<span id="cb2-6"><a href="#cb2-6" tabindex="-1"></a>pi</span>
<span id="cb2-7"><a href="#cb2-7" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span>
<span id="cb2-8"><a href="#cb2-8" tabindex="-1"></a></span>
<span id="cb2-9"><a href="#cb2-9" tabindex="-1"></a><span class="fu">sloppy</span>(pi, <span class="dv">2</span>)</span>
<span id="cb2-10"><a href="#cb2-10" tabindex="-1"></a><span class="co">#&gt; [1] 3.1</span></span>
<span id="cb2-11"><a href="#cb2-11" tabindex="-1"></a></span>
<span id="cb2-12"><a href="#cb2-12" tabindex="-1"></a>pi</span>
<span id="cb2-13"><a href="#cb2-13" tabindex="-1"></a><span class="co">#&gt; [1] 3.1</span></span></code></pre></div>
<p>Notice how <code>pi</code> prints differently before and after the
call to <code>sloppy()</code>? Calling <code>sloppy()</code> has a side
effect: it changes the “digits” option globally, not just within its own
scope of operations. This is what we want to avoid.</p>
<p><em>Dont worry, were restoring global state (specifically, the
“digits” option) behind the scenes here.</em></p>
<p>Sometimes you cannot avoid modifying the state of the world, in which
case you just have to make sure that you put things back the way you
found them. This is what the withr package is for.</p>
</div>
<div id="the-base-solution-on.exit" class="section level2">
<h2>The base solution: <code>on.exit()</code></h2>
<p>The first function to know about is base Rs <code>on.exit()</code>.
Inside your function body, every time you do something that should be
undone <strong>on exit</strong>, you immediately register the cleanup
code with <code>on.exit(expr, add = TRUE)</code><a href="#fn1" class="footnote-ref" id="fnref1"><sup>1</sup></a>.</p>
<p><code>neat()</code> is an improvement over <code>sloppy()</code>,
because it uses <code>on.exit()</code> to ensure that the “digits”
option is restored to its original value.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb3-1"><a href="#cb3-1" tabindex="-1"></a>neat <span class="ot">&lt;-</span> <span class="cf">function</span>(x, sig_digits) {</span>
<span id="cb3-2"><a href="#cb3-2" tabindex="-1"></a> op <span class="ot">&lt;-</span> <span class="fu">options</span>(<span class="at">digits =</span> sig_digits)</span>
<span id="cb3-3"><a href="#cb3-3" tabindex="-1"></a> <span class="fu">on.exit</span>(<span class="fu">options</span>(op), <span class="at">add =</span> <span class="cn">TRUE</span>)</span>
<span id="cb3-4"><a href="#cb3-4" tabindex="-1"></a> <span class="fu">print</span>(x)</span>
<span id="cb3-5"><a href="#cb3-5" tabindex="-1"></a>}</span>
<span id="cb3-6"><a href="#cb3-6" tabindex="-1"></a></span>
<span id="cb3-7"><a href="#cb3-7" tabindex="-1"></a>pi</span>
<span id="cb3-8"><a href="#cb3-8" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span>
<span id="cb3-9"><a href="#cb3-9" tabindex="-1"></a></span>
<span id="cb3-10"><a href="#cb3-10" tabindex="-1"></a><span class="fu">neat</span>(pi, <span class="dv">2</span>)</span>
<span id="cb3-11"><a href="#cb3-11" tabindex="-1"></a><span class="co">#&gt; [1] 3.1</span></span>
<span id="cb3-12"><a href="#cb3-12" tabindex="-1"></a></span>
<span id="cb3-13"><a href="#cb3-13" tabindex="-1"></a>pi</span>
<span id="cb3-14"><a href="#cb3-14" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span></code></pre></div>
<p><code>on.exit()</code> also works when you exit the function
abnormally, i.e. due to error. This is why official tools, like
<code>on.exit()</code>, are a better choice than any do-it-yourself
solution to this problem.</p>
<p><code>on.exit()</code> is a very useful function, but its not very
flexible. The withr package provides an extensible
<code>on.exit()</code>-inspired toolkit.</p>
</div>
<div id="defer-is-the-foundation-of-withr" class="section level2">
<h2><code>defer()</code> is the foundation of withr</h2>
<p><code>defer()</code> is the core function of withr and is very much
like <code>on.exit()</code>, i.e. it schedules the execution of
arbitrary code when the current function exits:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb4-1"><a href="#cb4-1" tabindex="-1"></a>neater <span class="ot">&lt;-</span> <span class="cf">function</span>(x, sig_digits) {</span>
<span id="cb4-2"><a href="#cb4-2" tabindex="-1"></a> op <span class="ot">&lt;-</span> <span class="fu">options</span>(<span class="at">digits =</span> sig_digits)</span>
<span id="cb4-3"><a href="#cb4-3" tabindex="-1"></a> <span class="fu">defer</span>(<span class="fu">options</span>(op))</span>
<span id="cb4-4"><a href="#cb4-4" tabindex="-1"></a> <span class="fu">print</span>(x)</span>
<span id="cb4-5"><a href="#cb4-5" tabindex="-1"></a>}</span>
<span id="cb4-6"><a href="#cb4-6" tabindex="-1"></a></span>
<span id="cb4-7"><a href="#cb4-7" tabindex="-1"></a>pi</span>
<span id="cb4-8"><a href="#cb4-8" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span>
<span id="cb4-9"><a href="#cb4-9" tabindex="-1"></a></span>
<span id="cb4-10"><a href="#cb4-10" tabindex="-1"></a><span class="fu">neater</span>(pi, <span class="dv">2</span>)</span>
<span id="cb4-11"><a href="#cb4-11" tabindex="-1"></a><span class="co">#&gt; [1] 3.1</span></span>
<span id="cb4-12"><a href="#cb4-12" tabindex="-1"></a></span>
<span id="cb4-13"><a href="#cb4-13" tabindex="-1"></a>pi</span>
<span id="cb4-14"><a href="#cb4-14" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span></code></pre></div>
<p><code>withr::defer()</code> is basically a drop-in substitute for
<code>on.exit()</code>, but with three key differences we explore
below:</p>
<ol style="list-style-type: decimal">
<li>Different default behaviour around the effect of a series of two or
more calls</li>
<li>Control over the environment the deferred events are associated
with</li>
<li>Ability to work with the global environment</li>
</ol>
<p>Here we focus on using withr inside your functions. See the blog post
<a href="https://www.tidyverse.org/blog/2020/04/self-cleaning-test-fixtures/">Self-cleaning
test fixtures</a> or the testthat vignette <a href="https://testthat.r-lib.org/articles/test-fixtures.html">Test
fixtures</a> for how to use withr inside tests.</p>
</div>
<div id="last-in-first-out" class="section level2">
<h2>Last-in, first-out</h2>
<p>If you make more than one call to <code>defer()</code>, by default,
it <strong>adds</strong> expressions to the <strong>top</strong> of the
stack of deferred actions.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb5-1"><a href="#cb5-1" tabindex="-1"></a>defer_stack <span class="ot">&lt;-</span> <span class="cf">function</span>() {</span>
<span id="cb5-2"><a href="#cb5-2" tabindex="-1"></a> <span class="fu">cat</span>(<span class="st">&quot;put on socks</span><span class="sc">\n</span><span class="st">&quot;</span>)</span>
<span id="cb5-3"><a href="#cb5-3" tabindex="-1"></a> <span class="fu">defer</span>(<span class="fu">cat</span>(<span class="st">&quot;take off socks</span><span class="sc">\n</span><span class="st">&quot;</span>))</span>
<span id="cb5-4"><a href="#cb5-4" tabindex="-1"></a> </span>
<span id="cb5-5"><a href="#cb5-5" tabindex="-1"></a> <span class="fu">cat</span>(<span class="st">&quot;put on shoes</span><span class="sc">\n</span><span class="st">&quot;</span>)</span>
<span id="cb5-6"><a href="#cb5-6" tabindex="-1"></a> <span class="fu">defer</span>(<span class="fu">cat</span>(<span class="st">&quot;take off shoes</span><span class="sc">\n</span><span class="st">&quot;</span>))</span>
<span id="cb5-7"><a href="#cb5-7" tabindex="-1"></a>}</span>
<span id="cb5-8"><a href="#cb5-8" tabindex="-1"></a><span class="fu">defer_stack</span>()</span>
<span id="cb5-9"><a href="#cb5-9" tabindex="-1"></a><span class="co">#&gt; put on socks</span></span>
<span id="cb5-10"><a href="#cb5-10" tabindex="-1"></a><span class="co">#&gt; put on shoes</span></span>
<span id="cb5-11"><a href="#cb5-11" tabindex="-1"></a><span class="co">#&gt; take off shoes</span></span>
<span id="cb5-12"><a href="#cb5-12" tabindex="-1"></a><span class="co">#&gt; take off socks</span></span></code></pre></div>
<p>In contrast, by default, a subsequent call to <code>on.exit()</code>
<strong>overwrites</strong> the deferred actions registered in the
previous call.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb6-1"><a href="#cb6-1" tabindex="-1"></a>on_exit_last_one_wins <span class="ot">&lt;-</span> <span class="cf">function</span>() {</span>
<span id="cb6-2"><a href="#cb6-2" tabindex="-1"></a> <span class="fu">cat</span>(<span class="st">&quot;put on socks</span><span class="sc">\n</span><span class="st">&quot;</span>)</span>
<span id="cb6-3"><a href="#cb6-3" tabindex="-1"></a> <span class="fu">on.exit</span>(<span class="fu">cat</span>(<span class="st">&quot;take off socks</span><span class="sc">\n</span><span class="st">&quot;</span>))</span>
<span id="cb6-4"><a href="#cb6-4" tabindex="-1"></a> </span>
<span id="cb6-5"><a href="#cb6-5" tabindex="-1"></a> <span class="fu">cat</span>(<span class="st">&quot;put on shoes</span><span class="sc">\n</span><span class="st">&quot;</span>)</span>
<span id="cb6-6"><a href="#cb6-6" tabindex="-1"></a> <span class="fu">on.exit</span>(<span class="fu">cat</span>(<span class="st">&quot;take off shoes</span><span class="sc">\n</span><span class="st">&quot;</span>))</span>
<span id="cb6-7"><a href="#cb6-7" tabindex="-1"></a>}</span>
<span id="cb6-8"><a href="#cb6-8" tabindex="-1"></a><span class="fu">on_exit_last_one_wins</span>()</span>
<span id="cb6-9"><a href="#cb6-9" tabindex="-1"></a><span class="co">#&gt; put on socks</span></span>
<span id="cb6-10"><a href="#cb6-10" tabindex="-1"></a><span class="co">#&gt; put on shoes</span></span>
<span id="cb6-11"><a href="#cb6-11" tabindex="-1"></a><span class="co">#&gt; take off shoes</span></span></code></pre></div>
<p>Oops, we still have our socks on! The last-in, first-out, stack-like
behaviour of <code>defer()</code> tends to be what you want in most
applications.</p>
<p>To get such behaviour with <code>on.exit()</code>, remember to call
it with <code>add = TRUE, after = FALSE</code><a href="#fn2" class="footnote-ref" id="fnref2"><sup>2</sup></a>.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb7-1"><a href="#cb7-1" tabindex="-1"></a>on_exit_stack <span class="ot">&lt;-</span> <span class="cf">function</span>() {</span>
<span id="cb7-2"><a href="#cb7-2" tabindex="-1"></a> <span class="fu">cat</span>(<span class="st">&quot;put on socks</span><span class="sc">\n</span><span class="st">&quot;</span>)</span>
<span id="cb7-3"><a href="#cb7-3" tabindex="-1"></a> <span class="fu">on.exit</span>(<span class="fu">cat</span>(<span class="st">&quot;take off socks</span><span class="sc">\n</span><span class="st">&quot;</span>), <span class="at">add =</span> <span class="cn">TRUE</span>, <span class="at">after =</span> <span class="cn">FALSE</span>)</span>
<span id="cb7-4"><a href="#cb7-4" tabindex="-1"></a> </span>
<span id="cb7-5"><a href="#cb7-5" tabindex="-1"></a> <span class="fu">cat</span>(<span class="st">&quot;put on shoes</span><span class="sc">\n</span><span class="st">&quot;</span>)</span>
<span id="cb7-6"><a href="#cb7-6" tabindex="-1"></a> <span class="fu">on.exit</span>(<span class="fu">cat</span>(<span class="st">&quot;take off shoes</span><span class="sc">\n</span><span class="st">&quot;</span>), <span class="at">add =</span> <span class="cn">TRUE</span>, <span class="at">after =</span> <span class="cn">FALSE</span>)</span>
<span id="cb7-7"><a href="#cb7-7" tabindex="-1"></a>}</span>
<span id="cb7-8"><a href="#cb7-8" tabindex="-1"></a><span class="fu">on_exit_stack</span>()</span>
<span id="cb7-9"><a href="#cb7-9" tabindex="-1"></a><span class="co">#&gt; put on socks</span></span>
<span id="cb7-10"><a href="#cb7-10" tabindex="-1"></a><span class="co">#&gt; put on shoes</span></span>
<span id="cb7-11"><a href="#cb7-11" tabindex="-1"></a><span class="co">#&gt; take off shoes</span></span>
<span id="cb7-12"><a href="#cb7-12" tabindex="-1"></a><span class="co">#&gt; take off socks</span></span></code></pre></div>
<p>Conversely, if you want <code>defer()</code> to have first-in,
first-out behaviour, specify <code>priority = &quot;last&quot;</code>.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb8-1"><a href="#cb8-1" tabindex="-1"></a>defer_queue <span class="ot">&lt;-</span> <span class="cf">function</span>() {</span>
<span id="cb8-2"><a href="#cb8-2" tabindex="-1"></a> <span class="fu">cat</span>(<span class="st">&quot;Adam gets in line for ice cream</span><span class="sc">\n</span><span class="st">&quot;</span>)</span>
<span id="cb8-3"><a href="#cb8-3" tabindex="-1"></a> <span class="fu">defer</span>(<span class="fu">cat</span>(<span class="st">&quot;Adam gets ice cream</span><span class="sc">\n</span><span class="st">&quot;</span>), <span class="at">priority =</span> <span class="st">&quot;last&quot;</span>)</span>
<span id="cb8-4"><a href="#cb8-4" tabindex="-1"></a></span>
<span id="cb8-5"><a href="#cb8-5" tabindex="-1"></a> <span class="fu">cat</span>(<span class="st">&quot;Beth gets in line for ice cream</span><span class="sc">\n</span><span class="st">&quot;</span>)</span>
<span id="cb8-6"><a href="#cb8-6" tabindex="-1"></a> <span class="fu">defer</span>(<span class="fu">cat</span>(<span class="st">&quot;Beth gets ice cream</span><span class="sc">\n</span><span class="st">&quot;</span>), <span class="at">priority =</span> <span class="st">&quot;last&quot;</span>)</span>
<span id="cb8-7"><a href="#cb8-7" tabindex="-1"></a>}</span>
<span id="cb8-8"><a href="#cb8-8" tabindex="-1"></a><span class="fu">defer_queue</span>()</span>
<span id="cb8-9"><a href="#cb8-9" tabindex="-1"></a><span class="co">#&gt; Adam gets in line for ice cream</span></span>
<span id="cb8-10"><a href="#cb8-10" tabindex="-1"></a><span class="co">#&gt; Beth gets in line for ice cream</span></span>
<span id="cb8-11"><a href="#cb8-11" tabindex="-1"></a><span class="co">#&gt; Adam gets ice cream</span></span>
<span id="cb8-12"><a href="#cb8-12" tabindex="-1"></a><span class="co">#&gt; Beth gets ice cream</span></span></code></pre></div>
</div>
<div id="local-functions-and-with-functions" class="section level2">
<h2>“Local” functions (and “with” functions)</h2>
<p>Both <code>on.exit()</code> and <code>withr::defer()</code> schedule
actions to be executed when a certain environment goes out of scope,
most typically the execution environment of a function. But the
<code>envir</code> argument of <code>withr::defer()</code> lets you
specify a <em>different</em> environment, which makes it possible to
create customised <code>on.exit()</code> extensions.</p>
<p>Lets look at the <code>neater()</code> function again.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb9-1"><a href="#cb9-1" tabindex="-1"></a>neater <span class="ot">&lt;-</span> <span class="cf">function</span>(x, sig_digits) {</span>
<span id="cb9-2"><a href="#cb9-2" tabindex="-1"></a> op <span class="ot">&lt;-</span> <span class="fu">options</span>(<span class="at">digits =</span> sig_digits) <span class="co"># record orig. &quot;digits&quot; &amp; change &quot;digits&quot;</span></span>
<span id="cb9-3"><a href="#cb9-3" tabindex="-1"></a> <span class="fu">defer</span>(<span class="fu">options</span>(op)) <span class="co"># schedule restoration of &quot;digits&quot;</span></span>
<span id="cb9-4"><a href="#cb9-4" tabindex="-1"></a> </span>
<span id="cb9-5"><a href="#cb9-5" tabindex="-1"></a> <span class="fu">print</span>(x)</span>
<span id="cb9-6"><a href="#cb9-6" tabindex="-1"></a>}</span></code></pre></div>
<p>The first two lines are typical <code>on.exit()</code> maneuvers
where, in some order, you record an original state, arrange for its
eventual restoration, and change it. In real life, this can be much more
involved and you might want to wrap this logic up into a helper
function. You cant wrap <code>on.exit()</code> in this way, because
theres no way to reach back up into the correct parent frame and
schedule cleanup there. But with <code>defer()</code>, we can! Here is
such a custom helper, called <code>local_digits()</code>.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb10-1"><a href="#cb10-1" tabindex="-1"></a>local_digits <span class="ot">&lt;-</span> <span class="cf">function</span>(sig_digits, <span class="at">envir =</span> <span class="fu">parent.frame</span>()) {</span>
<span id="cb10-2"><a href="#cb10-2" tabindex="-1"></a> op <span class="ot">&lt;-</span> <span class="fu">options</span>(<span class="at">digits =</span> sig_digits)</span>
<span id="cb10-3"><a href="#cb10-3" tabindex="-1"></a> <span class="fu">defer</span>(<span class="fu">options</span>(op), <span class="at">envir =</span> envir)</span>
<span id="cb10-4"><a href="#cb10-4" tabindex="-1"></a>}</span></code></pre></div>
<p>We can use <code>local_digits()</code> to keep any manipulation of
<code>digits</code> local to a function.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb11-1"><a href="#cb11-1" tabindex="-1"></a>neato <span class="ot">&lt;-</span> <span class="cf">function</span>(x, digits) {</span>
<span id="cb11-2"><a href="#cb11-2" tabindex="-1"></a> <span class="fu">local_digits</span>(digits)</span>
<span id="cb11-3"><a href="#cb11-3" tabindex="-1"></a> <span class="fu">print</span>(x)</span>
<span id="cb11-4"><a href="#cb11-4" tabindex="-1"></a>}</span>
<span id="cb11-5"><a href="#cb11-5" tabindex="-1"></a></span>
<span id="cb11-6"><a href="#cb11-6" tabindex="-1"></a>pi</span>
<span id="cb11-7"><a href="#cb11-7" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span>
<span id="cb11-8"><a href="#cb11-8" tabindex="-1"></a></span>
<span id="cb11-9"><a href="#cb11-9" tabindex="-1"></a><span class="fu">neato</span>(pi, <span class="dv">2</span>)</span>
<span id="cb11-10"><a href="#cb11-10" tabindex="-1"></a><span class="co">#&gt; [1] 3.1</span></span>
<span id="cb11-11"><a href="#cb11-11" tabindex="-1"></a></span>
<span id="cb11-12"><a href="#cb11-12" tabindex="-1"></a><span class="fu">neato</span>(pi, <span class="dv">4</span>)</span>
<span id="cb11-13"><a href="#cb11-13" tabindex="-1"></a><span class="co">#&gt; [1] 3.142</span></span></code></pre></div>
<p>You can even call <code>local_digits()</code> multiple times inside a
function. Each call to <code>local_digits()</code> is in effect until
the next or until the function exits, which ever comes first.</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb12-1"><a href="#cb12-1" tabindex="-1"></a>neatful <span class="ot">&lt;-</span> <span class="cf">function</span>(x) {</span>
<span id="cb12-2"><a href="#cb12-2" tabindex="-1"></a> <span class="fu">local_digits</span>(<span class="dv">1</span>)</span>
<span id="cb12-3"><a href="#cb12-3" tabindex="-1"></a> <span class="fu">print</span>(x)</span>
<span id="cb12-4"><a href="#cb12-4" tabindex="-1"></a> <span class="fu">local_digits</span>(<span class="dv">3</span>)</span>
<span id="cb12-5"><a href="#cb12-5" tabindex="-1"></a> <span class="fu">print</span>(x)</span>
<span id="cb12-6"><a href="#cb12-6" tabindex="-1"></a> <span class="fu">local_digits</span>(<span class="dv">5</span>)</span>
<span id="cb12-7"><a href="#cb12-7" tabindex="-1"></a> <span class="fu">print</span>(x)</span>
<span id="cb12-8"><a href="#cb12-8" tabindex="-1"></a>}</span>
<span id="cb12-9"><a href="#cb12-9" tabindex="-1"></a></span>
<span id="cb12-10"><a href="#cb12-10" tabindex="-1"></a><span class="fu">neatful</span>(pi)</span>
<span id="cb12-11"><a href="#cb12-11" tabindex="-1"></a><span class="co">#&gt; [1] 3</span></span>
<span id="cb12-12"><a href="#cb12-12" tabindex="-1"></a><span class="co">#&gt; [1] 3.14</span></span>
<span id="cb12-13"><a href="#cb12-13" tabindex="-1"></a><span class="co">#&gt; [1] 3.1416</span></span></code></pre></div>
<p>Certain state changes, such as modifying global options, come up so
often that withr offers pre-made helpers. These helpers come in two
forms: <code>local_*()</code> functions, like the one we just made, and
<code>with_*()</code> functions, which we explain below. Here are the
state change helpers in withr that you are most likely to find
useful:</p>
<table>
<thead>
<tr class="header">
<th>Do / undo this</th>
<th>withr functions</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Set an R option</td>
<td><code>local_options()</code>,<code>with_options()</code></td>
</tr>
<tr class="even">
<td>Set an environment variable</td>
<td><code>local_envvar()</code>, <code>with_envvar()</code></td>
</tr>
<tr class="odd">
<td>Change working directory</td>
<td><code>local_dir()</code>, <code>with_dir()</code></td>
</tr>
<tr class="even">
<td>Set a graphics parameter</td>
<td><code>local_par()</code>, <code>with_par()</code></td>
</tr>
</tbody>
</table>
<p>We didnt really need to write our own <code>local_digits()</code>
helper, because the built-in <code>withr::local_options()</code> also
gets the job done:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb13-1"><a href="#cb13-1" tabindex="-1"></a>neatest <span class="ot">&lt;-</span> <span class="cf">function</span>(x, sig_digits) {</span>
<span id="cb13-2"><a href="#cb13-2" tabindex="-1"></a> <span class="fu">local_options</span>(<span class="fu">list</span>(<span class="at">digits =</span> sig_digits))</span>
<span id="cb13-3"><a href="#cb13-3" tabindex="-1"></a> <span class="fu">print</span>(x)</span>
<span id="cb13-4"><a href="#cb13-4" tabindex="-1"></a>}</span>
<span id="cb13-5"><a href="#cb13-5" tabindex="-1"></a></span>
<span id="cb13-6"><a href="#cb13-6" tabindex="-1"></a>pi</span>
<span id="cb13-7"><a href="#cb13-7" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span>
<span id="cb13-8"><a href="#cb13-8" tabindex="-1"></a></span>
<span id="cb13-9"><a href="#cb13-9" tabindex="-1"></a><span class="fu">neatest</span>(pi, <span class="dv">2</span>)</span>
<span id="cb13-10"><a href="#cb13-10" tabindex="-1"></a><span class="co">#&gt; [1] 3.1</span></span>
<span id="cb13-11"><a href="#cb13-11" tabindex="-1"></a></span>
<span id="cb13-12"><a href="#cb13-12" tabindex="-1"></a><span class="fu">neatest</span>(pi, <span class="dv">4</span>)</span>
<span id="cb13-13"><a href="#cb13-13" tabindex="-1"></a><span class="co">#&gt; [1] 3.142</span></span></code></pre></div>
<p>The <code>local_*()</code> functions target a slightly different use
case from the <code>with_*()</code> functions, which are inspired by
base Rs <code>with()</code> function:</p>
<ul>
<li><p><code>with_*()</code> functions are best for executing a small
snippet of code with a modified state</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb14-1"><a href="#cb14-1" tabindex="-1"></a>neat_with <span class="ot">&lt;-</span> <span class="cf">function</span>(x, sig_digits) {</span>
<span id="cb14-2"><a href="#cb14-2" tabindex="-1"></a> <span class="co"># imagine lots of code here</span></span>
<span id="cb14-3"><a href="#cb14-3" tabindex="-1"></a> withr<span class="sc">::</span><span class="fu">with_options</span>(</span>
<span id="cb14-4"><a href="#cb14-4" tabindex="-1"></a> <span class="fu">list</span>(<span class="at">digits =</span> sig_digits),</span>
<span id="cb14-5"><a href="#cb14-5" tabindex="-1"></a> <span class="fu">print</span>(x)</span>
<span id="cb14-6"><a href="#cb14-6" tabindex="-1"></a> )</span>
<span id="cb14-7"><a href="#cb14-7" tabindex="-1"></a> <span class="co"># ... and a lot more code here</span></span>
<span id="cb14-8"><a href="#cb14-8" tabindex="-1"></a>}</span></code></pre></div></li>
<li><p><code>local_*()</code> functions are best for modifying state
“from now until the function exits”</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb15-1"><a href="#cb15-1" tabindex="-1"></a>neat_local <span class="ot">&lt;-</span> <span class="cf">function</span>(x, sig_digits) {</span>
<span id="cb15-2"><a href="#cb15-2" tabindex="-1"></a> withr<span class="sc">::</span><span class="fu">local_options</span>(<span class="fu">list</span>(<span class="at">digits =</span> sig_digits))</span>
<span id="cb15-3"><a href="#cb15-3" tabindex="-1"></a> <span class="fu">print</span>(x)</span>
<span id="cb15-4"><a href="#cb15-4" tabindex="-1"></a> <span class="co"># imagine lots of code here</span></span>
<span id="cb15-5"><a href="#cb15-5" tabindex="-1"></a>}</span></code></pre></div></li>
</ul>
<p>Its best to minimize the footprint of your state modifications.
Therefore, use <code>with_*()</code> functions where you can. But when
this forces you to put lots of (indented) code inside
<code>with_*()</code>, e.g. most of your functions body, then its
better to use <code>local_*()</code>.</p>
</div>
<div id="deferring-events-on-the-global-environment" class="section level2">
<h2>Deferring events on the global environment</h2>
<p>Here is one last difference between <code>withr::defer()</code> and
<code>on.exit()</code>: the ability to defer events on the global
environment<a href="#fn3" class="footnote-ref" id="fnref3"><sup>3</sup></a>.</p>
<p>At first, it sounds pretty weird to propose scheduling deferred
actions on the global environment. Its not ephemeral, the way function
execution environments are. It goes out of scope very rarely, i.e. when
you exit R. Why would you want this?</p>
<p>The answer is: for development purposes.</p>
<p>If you are developing functions or tests that use withr, its very
useful to be able to execute that code interactively, without error, and
with the ability to trigger the deferred events. Its hard to develop
with functions that work one way inside a function, but another way in
the global environment (or, worse, throw an error).</p>
<p>Heres how <code>defer()</code> (and all functions based on it) works
in an interactive session.</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb16-1"><a href="#cb16-1" tabindex="-1"></a><span class="fu">library</span>(withr)</span>
<span id="cb16-2"><a href="#cb16-2" tabindex="-1"></a></span>
<span id="cb16-3"><a href="#cb16-3" tabindex="-1"></a><span class="fu">defer</span>(<span class="fu">print</span>(<span class="st">&quot;hi&quot;</span>))</span>
<span id="cb16-4"><a href="#cb16-4" tabindex="-1"></a></span>
<span id="cb16-5"><a href="#cb16-5" tabindex="-1"></a>pi</span>
<span id="cb16-6"><a href="#cb16-6" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span>
<span id="cb16-7"><a href="#cb16-7" tabindex="-1"></a></span>
<span id="cb16-8"><a href="#cb16-8" tabindex="-1"></a><span class="co"># this adds another deferred event, but does not re-message</span></span>
<span id="cb16-9"><a href="#cb16-9" tabindex="-1"></a><span class="fu">local_digits</span>(<span class="dv">3</span>)</span>
<span id="cb16-10"><a href="#cb16-10" tabindex="-1"></a></span>
<span id="cb16-11"><a href="#cb16-11" tabindex="-1"></a>pi</span>
<span id="cb16-12"><a href="#cb16-12" tabindex="-1"></a><span class="co">#&gt; [1] 3.14</span></span>
<span id="cb16-13"><a href="#cb16-13" tabindex="-1"></a></span>
<span id="cb16-14"><a href="#cb16-14" tabindex="-1"></a><span class="fu">deferred_run</span>()</span>
<span id="cb16-15"><a href="#cb16-15" tabindex="-1"></a><span class="co">#&gt; [1] &quot;hi&quot;</span></span>
<span id="cb16-16"><a href="#cb16-16" tabindex="-1"></a><span class="co">#&gt; Ran 2/2 deferred expressions</span></span>
<span id="cb16-17"><a href="#cb16-17" tabindex="-1"></a></span>
<span id="cb16-18"><a href="#cb16-18" tabindex="-1"></a>pi</span>
<span id="cb16-19"><a href="#cb16-19" tabindex="-1"></a><span class="co">#&gt; [1] 3.141593</span></span></code></pre></div>
<p>Note that because this example is running in a vignette, it doesnt
look exactly the same as what youll see interactively. When you defer
events on the global environment for the first time, you get this
message that alerts you to the situation:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb17-1"><a href="#cb17-1" tabindex="-1"></a><span class="fu">defer</span>(<span class="fu">print</span>(<span class="st">&quot;hi&quot;</span>))</span>
<span id="cb17-2"><a href="#cb17-2" tabindex="-1"></a><span class="co">#&gt; Setting global deferred event(s).</span></span>
<span id="cb17-3"><a href="#cb17-3" tabindex="-1"></a><span class="co">#&gt; i These will be run:</span></span>
<span id="cb17-4"><a href="#cb17-4" tabindex="-1"></a><span class="co">#&gt; * Automatically, when the R session ends.</span></span>
<span id="cb17-5"><a href="#cb17-5" tabindex="-1"></a><span class="co">#&gt; * On demand, if you call `withr::deferred_run()`.</span></span>
<span id="cb17-6"><a href="#cb17-6" tabindex="-1"></a><span class="co">#&gt; i Use `withr::deferred_clear()` to clear them without executing.</span></span></code></pre></div>
<p>If you add subsequent events, the message is <em>not</em> repeated.
Since the global environment isnt perishable, like a test environment
is, you have to call <code>deferred_run()</code> explicitly to execute
the deferred events. You can also clear them, without running, with
<code>deferred_clear()</code>.</p>
</div>
<div class="footnotes footnotes-end-of-document">
<hr />
<ol>
<li id="fn1"><p>Its too bad <code>add = TRUE</code> isnt the default,
because you almost always want this. Without it, each call to
<code>on.exit()</code> clobbers the effect of previous calls.<a href="#fnref1" class="footnote-back">↩︎</a></p></li>
<li id="fn2"><p>Note: the <code>after</code> argument of
<code>on.exit()</code> first appeared in R 3.5.0.<a href="#fnref2" class="footnote-back">↩︎</a></p></li>
<li id="fn3"><p>This feature first appeared in withr v2.2.0.<a href="#fnref3" class="footnote-back">↩︎</a></p></li>
</ol>
</div>
<!-- code folding -->
<!-- dynamically load mathjax for compatibility with self-contained -->
<script>
(function () {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
document.getElementsByTagName("head")[0].appendChild(script);
})();
</script>
</body>
</html>