636 lines
60 KiB
HTML
636 lines
60 KiB
HTML
<!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" />
|
||
|
||
<meta name="author" content="Hadley Wickham" />
|
||
<meta name="author" content="Lionel Henry" />
|
||
|
||
|
||
<title>Design tradeoffs</title>
|
||
|
||
<script src="data:application/javascript;base64,Ly8gUGFuZG9jIDIuOSBhZGRzIGF0dHJpYnV0ZXMgb24gYm90aCBoZWFkZXIgYW5kIGRpdi4gV2UgcmVtb3ZlIHRoZSBmb3JtZXIgKHRvCi8vIGJlIGNvbXBhdGlibGUgd2l0aCB0aGUgYmVoYXZpb3Igb2YgUGFuZG9jIDwgMi44KS4KZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGZ1bmN0aW9uKGUpIHsKICB2YXIgaHMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCJkaXYuc2VjdGlvbltjbGFzcyo9J2xldmVsJ10gPiA6Zmlyc3QtY2hpbGQiKTsKICB2YXIgaSwgaCwgYTsKICBmb3IgKGkgPSAwOyBpIDwgaHMubGVuZ3RoOyBpKyspIHsKICAgIGggPSBoc1tpXTsKICAgIGlmICghL15oWzEtNl0kL2kudGVzdChoLnRhZ05hbWUpKSBjb250aW51ZTsgIC8vIGl0IHNob3VsZCBiZSBhIGhlYWRlciBoMS1oNgogICAgYSA9IGguYXR0cmlidXRlczsKICAgIHdoaWxlIChhLmxlbmd0aCA+IDApIGgucmVtb3ZlQXR0cmlidXRlKGFbMF0ubmFtZSk7CiAgfQp9KTsK"></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 { display: inline-block; 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 { 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; } /* Alert */
|
||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||
code span.at { color: #7d9029; } /* Attribute */
|
||
code span.bn { color: #40a070; } /* BaseN */
|
||
code span.bu { } /* BuiltIn */
|
||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||
code span.ch { color: #4070a0; } /* Char */
|
||
code span.cn { color: #880000; } /* Constant */
|
||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||
code span.dt { color: #902000; } /* DataType */
|
||
code span.dv { color: #40a070; } /* DecVal */
|
||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||
code span.ex { } /* Extension */
|
||
code span.fl { color: #40a070; } /* Float */
|
||
code span.fu { color: #06287e; } /* Function */
|
||
code span.im { } /* Import */
|
||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||
code span.op { color: #666666; } /* Operator */
|
||
code span.ot { color: #007020; } /* Other */
|
||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||
code span.ss { color: #bb6688; } /* SpecialString */
|
||
code span.st { color: #4070a0; } /* String */
|
||
code span.va { color: #19177c; } /* Variable */
|
||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||
|
||
</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; }
|
||
for (var j = 0; j < rules.length; j++) {
|
||
var rule = rules[j];
|
||
// check if there is a div.sourceCode rule
|
||
if (rule.type !== rule.STYLE_RULE || rule.selectorText !== "div.sourceCode") continue;
|
||
var style = rule.style.cssText;
|
||
// check if color or background-color is set
|
||
if (rule.style.color === '' && rule.style.backgroundColor === '') continue;
|
||
// replace div.sourceCode by a pre.sourceCode rule
|
||
sheets[i].deleteRule(j);
|
||
sheets[i].insertRule('pre.sourceCode{' + style + '}', j);
|
||
}
|
||
}
|
||
})();
|
||
</script>
|
||
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="data:text/css,body%20%7B%0Abackground%2Dcolor%3A%20%23fff%3B%0Amargin%3A%201em%20auto%3B%0Amax%2Dwidth%3A%20700px%3B%0Aoverflow%3A%20visible%3B%0Apadding%2Dleft%3A%202em%3B%0Apadding%2Dright%3A%202em%3B%0Afont%2Dfamily%3A%20%22Open%20Sans%22%2C%20%22Helvetica%20Neue%22%2C%20Helvetica%2C%20Arial%2C%20sans%2Dserif%3B%0Afont%2Dsize%3A%2014px%3B%0Aline%2Dheight%3A%201%2E35%3B%0A%7D%0A%23TOC%20%7B%0Aclear%3A%20both%3B%0Amargin%3A%200%200%2010px%2010px%3B%0Apadding%3A%204px%3B%0Awidth%3A%20400px%3B%0Aborder%3A%201px%20solid%20%23CCCCCC%3B%0Aborder%2Dradius%3A%205px%3B%0Abackground%2Dcolor%3A%20%23f6f6f6%3B%0Afont%2Dsize%3A%2013px%3B%0Aline%2Dheight%3A%201%2E3%3B%0A%7D%0A%23TOC%20%2Etoctitle%20%7B%0Afont%2Dweight%3A%20bold%3B%0Afont%2Dsize%3A%2015px%3B%0Amargin%2Dleft%3A%205px%3B%0A%7D%0A%23TOC%20ul%20%7B%0Apadding%2Dleft%3A%2040px%3B%0Amargin%2Dleft%3A%20%2D1%2E5em%3B%0Amargin%2Dtop%3A%205px%3B%0Amargin%2Dbottom%3A%205px%3B%0A%7D%0A%23TOC%20ul%20ul%20%7B%0Amargin%2Dleft%3A%20%2D2em%3B%0A%7D%0A%23TOC%20li%20%7B%0Aline%2Dheight%3A%2016px%3B%0A%7D%0Atable%20%7B%0Amargin%3A%201em%20auto%3B%0Aborder%2Dwidth%3A%201px%3B%0Aborder%2Dcolor%3A%20%23DDDDDD%3B%0Aborder%2Dstyle%3A%20outset%3B%0Aborder%2Dcollapse%3A%20collapse%3B%0A%7D%0Atable%20th%20%7B%0Aborder%2Dwidth%3A%202px%3B%0Apadding%3A%205px%3B%0Aborder%2Dstyle%3A%20inset%3B%0A%7D%0Atable%20td%20%7B%0Aborder%2Dwidth%3A%201px%3B%0Aborder%2Dstyle%3A%20inset%3B%0Aline%2Dheight%3A%2018px%3B%0Apadding%3A%205px%205px%3B%0A%7D%0Atable%2C%20table%20th%2C%20table%20td%20%7B%0Aborder%2Dleft%2Dstyle%3A%20none%3B%0Aborder%2Dright%2Dstyle%3A%20none%3B%0A%7D%0Atable%20thead%2C%20table%20tr%2Eeven%20%7B%0Abackground%2Dcolor%3A%20%23f7f7f7%3B%0A%7D%0Ap%20%7B%0Amargin%3A%200%2E5em%200%3B%0A%7D%0Ablockquote%20%7B%0Abackground%2Dcolor%3A%20%23f6f6f6%3B%0Apadding%3A%200%2E25em%200%2E75em%3B%0A%7D%0Ahr%20%7B%0Aborder%2Dstyle%3A%20solid%3B%0Aborder%3A%20none%3B%0Aborder%2Dtop%3A%201px%20solid%20%23777%3B%0Amargin%3A%2028px%200%3B%0A%7D%0Adl%20%7B%0Amargin%2Dleft%3A%200%3B%0A%7D%0Adl%20dd%20%7B%0Amargin%2Dbottom%3A%2013px%3B%0Amargin%2Dleft%3A%2013px%3B%0A%7D%0Adl%20dt%20%7B%0Afont%2Dweight%3A%20bold%3B%0A%7D%0Aul%20%7B%0Amargin%2Dtop%3A%200%3B%0A%7D%0Aul%20li%20%7B%0Alist%2Dstyle%3A%20circle%20outside%3B%0A%7D%0Aul%20ul%20%7B%0Amargin%2Dbottom%3A%200%3B%0A%7D%0Apre%2C%20code%20%7B%0Abackground%2Dcolor%3A%20%23f7f7f7%3B%0Aborder%2Dradius%3A%203px%3B%0Acolor%3A%20%23333%3B%0Awhite%2Dspace%3A%20pre%2Dwrap%3B%20%0A%7D%0Apre%20%7B%0Aborder%2Dradius%3A%203px%3B%0Amargin%3A%205px%200px%2010px%200px%3B%0Apadding%3A%2010px%3B%0A%7D%0Apre%3Anot%28%5Bclass%5D%29%20%7B%0Abackground%2Dcolor%3A%20%23f7f7f7%3B%0A%7D%0Acode%20%7B%0Afont%2Dfamily%3A%20Consolas%2C%20Monaco%2C%20%27Courier%20New%27%2C%20monospace%3B%0Afont%2Dsize%3A%2085%25%3B%0A%7D%0Ap%20%3E%20code%2C%20li%20%3E%20code%20%7B%0Apadding%3A%202px%200px%3B%0A%7D%0Adiv%2Efigure%20%7B%0Atext%2Dalign%3A%20center%3B%0A%7D%0Aimg%20%7B%0Abackground%2Dcolor%3A%20%23FFFFFF%3B%0Apadding%3A%202px%3B%0Aborder%3A%201px%20solid%20%23DDDDDD%3B%0Aborder%2Dradius%3A%203px%3B%0Aborder%3A%201px%20solid%20%23CCCCCC%3B%0Amargin%3A%200%205px%3B%0A%7D%0Ah1%20%7B%0Amargin%2Dtop%3A%200%3B%0Afont%2Dsize%3A%2035px%3B%0Aline%2Dheight%3A%2040px%3B%0A%7D%0Ah2%20%7B%0Aborder%2Dbottom%3A%204px%20solid%20%23f7f7f7%3B%0Apadding%2Dtop%3A%2010px%3B%0Apadding%2Dbottom%3A%202px%3B%0Afont%2Dsize%3A%20145%25%3B%0A%7D%0Ah3%20%7B%0Aborder%2Dbottom%3A%202px%20solid%20%23f7f7f7%3B%0Apadding%2Dtop%3A%2010px%3B%0Afont%2Dsize%3A%20120%25%3B%0A%7D%0Ah4%20%7B%0Aborder%2Dbottom%3A%201px%20solid%20%23f7f7f7%3B%0Amargin%2Dleft%3A%208px%3B%0Afont%2Dsize%3A%20105%25%3B%0A%7D%0Ah5%2C%20h6%20%7B%0Aborder%2Dbottom%3A%201px%20solid%20%23ccc%3B%0Afont%2Dsize%3A%20105%25%3B%0A%7D%0Aa%20%7B%0Acolor%3A%20%230033dd%3B%0Atext%2Ddecoration%3A%20none%3B%0A%7D%0Aa%3Ahover%20%7B%0Acolor%3A%20%236666ff%3B%20%7D%0Aa%3Avisited%20%7B%0Acolor%3A%20%23800080%3B%20%7D%0Aa%3Avisited%3Ahover%20%7B%0Acolor%3A%20%23BB00BB%3B%20%7D%0Aa%5Bhref%5E%3D%22http%3A%22%5D%20%7B%0Atext%2Ddecoration%3A%20underline%3B%20%7D%0Aa%5Bhref%5E%3D%22https%3A%22%5D%20%7B%0Atext%2Ddecoration%3A%20underline%3B%20%7D%0A%0Acode%20%3E%20span%2Ekw%20%7B%20color%3A%20%23555%3B%20font%2Dweight%3A%20bold%3B%20%7D%20%0Acode%20%3E%20span%2Edt%20%7B%20color%3A%20%23902000%3B%20%7D%20%0Acode%20%3E%20span%2Edv%20%7B%20color%3A%20%2340a070%3B%20%7D%20%0Acode%20%3E%20span%2Ebn%20%7B%20color%3A%20%23d14%3B%20%7D%20%0Acode%20%3E%20span%2Efl%20%7B%20color%3A%20%23d14%3B%20%7D%20%0Acode%20%3E%20span%2Ech%20%7B%20color%3A%20%23d14%3B%20%7D%20%0Acode%20%3E%20span%2Est%20%7B%20color%3A%20%23d14%3B%20%7D%20%0Acode%20%3E%20span%2Eco%20%7B%20color%3A%20%23888888%3B%20font%2Dstyle%3A%20italic%3B%20%7D%20%0Acode%20%3E%20span%2Eot%20%7B%20color%3A%20%23007020%3B%20%7D%20%0Acode%20%3E%20span%2Eal%20%7B%20color%3A%20%23ff0000%3B%20font%2Dweight%3A%20bold%3B%20%7D%20%0Acode%20%3E%20span%2Efu%20%7B%20color%3A%20%23900%3B%20font%2Dweight%3A%20bold%3B%20%7D%20%0Acode%20%3E%20span%2Eer%20%7B%20color%3A%20%23a61717%3B%20background%2Dcolor%3A%20%23e3d2d2%3B%20%7D%20%0A" type="text/css" />
|
||
|
||
|
||
|
||
|
||
</head>
|
||
|
||
<body>
|
||
|
||
|
||
|
||
|
||
<h1 class="title toc-ignore">Design tradeoffs</h1>
|
||
<h4 class="author">Hadley Wickham</h4>
|
||
<h4 class="author">Lionel Henry</h4>
|
||
|
||
|
||
|
||
<p>There are many different ways that magrittr could implement the pipe. The goal of this document is to elucidate the variations, and the various pros and cons of each approach. This document is primarily aimed at the magrittr developers (so we don’t forget about important considerations), but will be of interest to anyone who wants to understand pipes better, or to create their own pipe that makes different tradeoffs</p>
|
||
<div id="code-transformation" class="section level2">
|
||
<h2>Code transformation</h2>
|
||
<p>There are three main options for how we might transform a pipeline in base R expressions. Here they are illustrated with <code>x %>% foo() %>% bar()</code>:</p>
|
||
<ul>
|
||
<li><p><strong>Nested</strong></p>
|
||
<div class="sourceCode" id="cb1"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">bar</span>(<span class="fu">foo</span>(x))</span></code></pre></div></li>
|
||
<li><p><strong>Eager (mask)</strong>, masking environment</p>
|
||
<p>This is essentially how <code>%>%</code> has been implemented prior to magrittr 2.0:</p>
|
||
<div class="sourceCode" id="cb2"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="fu">local</span>({</span>
|
||
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> . <span class="ot"><-</span> x</span>
|
||
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> . <span class="ot"><-</span> <span class="fu">foo</span>(.)</span>
|
||
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">bar</span>(.)</span>
|
||
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>})</span></code></pre></div></li>
|
||
<li><p><strong>Eager (mask-num)</strong>: masking environment, numbered placeholder</p>
|
||
<div class="sourceCode" id="cb3"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">local</span>({</span>
|
||
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> ...<span class="dv">1</span> <span class="ot"><-</span> x</span>
|
||
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> ...<span class="dv">2</span> <span class="ot"><-</span> <span class="fu">foo</span>(...<span class="dv">1</span>)</span>
|
||
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">bar</span>(...<span class="dv">2</span>)</span>
|
||
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>})</span></code></pre></div></li>
|
||
<li><p><strong>Eager (lexical)</strong>: lexical environment</p>
|
||
<p>This variant assigns pipe expressions to the placeholder <code>.</code> in the current environment. This assignment is temporary: once the pipe has returned, the placeholder binding is reset to its previous state.</p>
|
||
<div class="sourceCode" id="cb4"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>with_dot_cleanup <span class="ot"><-</span> <span class="cf">function</span>(expr) {</span>
|
||
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> <span class="co"># Initialises `.` in the caller environment and resets it on exit.</span></span>
|
||
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="co"># (We use `:=` instead of `=` to avoid partial matching.)</span></span>
|
||
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> rlang<span class="sc">::</span><span class="fu">local_bindings</span>(<span class="at">. :=</span> <span class="cn">NULL</span>, <span class="at">.env =</span> <span class="fu">parent.frame</span>())</span>
|
||
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> expr</span>
|
||
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>}</span>
|
||
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="fu">with_dot_cleanup</span>({</span>
|
||
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> . <span class="ot"><-</span> x</span>
|
||
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> . <span class="ot"><-</span> <span class="fu">foo</span>(.)</span>
|
||
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> <span class="fu">bar</span>(.)</span>
|
||
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>})</span></code></pre></div></li>
|
||
<li><p><strong>Lazy (mask)</strong>: masking environments</p>
|
||
<div class="sourceCode" id="cb5"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>mask1 <span class="ot"><-</span> <span class="fu">new.env</span>(<span class="at">parent =</span> env)</span>
|
||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>mask2 <span class="ot"><-</span> <span class="fu">new.env</span>(<span class="at">parent =</span> env)</span>
|
||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="fu">delayedAssign</span>(<span class="st">"."</span>, x, mask1)</span>
|
||
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="fu">delayedAssign</span>(<span class="st">"."</span>, <span class="fu">foo</span>(.), mask2)</span>
|
||
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="fu">with</span>(mask2, <span class="fu">bar</span>(.))</span></code></pre></div></li>
|
||
<li><p><strong>Lazy (mask-num)</strong>: masking environment, numbered placeholder</p>
|
||
<div class="sourceCode" id="cb6"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="fu">local</span>({</span>
|
||
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">delayedAssign</span>(<span class="st">"...1"</span>, x)</span>
|
||
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">delayedAssign</span>(<span class="st">"...2"</span>, <span class="fu">foo</span>(...<span class="dv">1</span>))</span>
|
||
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">bar</span>(...<span class="dv">2</span>)</span>
|
||
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>})</span></code></pre></div></li>
|
||
<li><p><strong>Lazy (lexical-num)</strong>: lexical environment, numbered placeholder</p>
|
||
<div class="sourceCode" id="cb7"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="fu">delayedAssign</span>(<span class="st">"...1"</span>, x)</span>
|
||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="fu">delayedAssign</span>(<span class="st">"...2"</span>, <span class="fu">foo</span>(.))</span>
|
||
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="fu">bar</span>(...<span class="dv">2</span>)</span></code></pre></div></li>
|
||
</ul>
|
||
<p>We’ll first explore the desired properties we might want a pipe to possess and then see how each of the three variants does.</p>
|
||
</div>
|
||
<div id="desired-properties" class="section level2">
|
||
<h2>Desired properties</h2>
|
||
<p>These are the properties that we might want a pipe to possess, roughly ordered from most important to least important.</p>
|
||
<ul>
|
||
<li><p><strong>Visibility</strong>: the visibility of the final function in the pipe should be preserved. This is important so that pipes that end in a side-effect function (which generally returns its first argument invisibly) do not print.</p></li>
|
||
<li><p><strong>Multiple placeholders</strong>: each component of the pipe should only be evaluated once even when there are multiple placeholders, so that <code>sample(10) %>% cbind(., .)</code> yields two columns with the same value. Relatedly, <code>sample(10) %T>% print() %T>% print()</code> must print the same values twice.</p></li>
|
||
<li><p><strong>Lazy evaluation</strong>: steps of the pipe should only be evaluated when actually needed. This is a useful property as it means that pipes can handle code like <code>stop("!") %>% try()</code>, making pipes capable of capturing a wider range of R expressions.</p>
|
||
<p>On the other hand, it might have surprising effects. For instance if a function that suppresses warnings is added to the end of a pipeline, the suppression takes effect for the whole pipeline.</p></li>
|
||
<li><p><strong>Persistence of piped values</strong>: arguments are not necessarily evaluated right away by the piped function. Sometimes they are evaluated long after the pipeline has returned, for example when a function factory is piped. With persistent piped values, the constructed function can be called at any time:</p>
|
||
<div class="sourceCode" id="cb8"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>factory <span class="ot"><-</span> <span class="cf">function</span>(x) <span class="cf">function</span>() x</span>
|
||
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>fn <span class="ot"><-</span> <span class="cn">NA</span> <span class="sc">%>%</span> <span class="fu">factory</span>()</span>
|
||
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="fu">fn</span>()</span>
|
||
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] NA</span></span></code></pre></div></li>
|
||
<li><p><strong>Refcount neutrality</strong>: the return value of the pipeline should have a reference count of 1 so it can be mutated in place in further manipulations.</p></li>
|
||
<li><p><strong>Eager unbinding</strong>: pipes are often used with large data objects, so intermediate objects in the pipeline should be unbound as soon as possible so they are available for garbage collection.</p></li>
|
||
<li><p><strong>Progressive stack</strong>: using the pipe should add as few entries to the call stack as possible, so that <code>traceback()</code> is maximally useful.</p></li>
|
||
<li><p><strong>Lexical side effects</strong>: side effects should occur in the current lexical environment. This way, <code>NA %>% { foo <- . }</code> assigns the piped value in the current environment and <code>NA %>% { return(.) }</code> returns from the function that contains the pipeline.</p></li>
|
||
<li><p><strong>Continuous stack</strong>: the pipe should not affect the chain of parent frames. This is important for tree representations of the call stack.</p></li>
|
||
</ul>
|
||
<p>It is possible to have proper visibility and a neutral impact on refcounts with all implementations by being a bit careful, so we’ll only consider the other properties:</p>
|
||
<table style="width:100%;">
|
||
<colgroup>
|
||
<col width="15%" />
|
||
<col width="7%" />
|
||
<col width="11%" />
|
||
<col width="13%" />
|
||
<col width="13%" />
|
||
<col width="10%" />
|
||
<col width="13%" />
|
||
<col width="15%" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr class="header">
|
||
<th></th>
|
||
<th align="center">Nested</th>
|
||
<th align="center">Eager<br>(mask)</th>
|
||
<th align="center">Eager<br>(mask-num)</th>
|
||
<th align="center">Eager<br>(lexical)</th>
|
||
<th align="center">Lazy<br>(mask)</th>
|
||
<th align="center">Lazy<br>(mask-num)</th>
|
||
<th align="center">Lazy<br>(lexical-num)</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td>Multiple placeholders</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td>Lazy evaluation</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td>Persistence</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td>Eager unbinding</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">❌</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td>Progressive stack</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">❌</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td>Lexical effects</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">✅</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td>Continuous stack</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">✅</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">❌</td>
|
||
<td align="center">✅</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<div id="implications-of-design-decisions" class="section level3">
|
||
<h3>Implications of design decisions</h3>
|
||
<p>Some properties are a direct reflection of the high level design decisions.</p>
|
||
<div id="placeholder-binding" class="section level4">
|
||
<h4>Placeholder binding</h4>
|
||
<p>The nested pipe does not assign piped expressions to a placeholder. All the other variants perform this assignment. This means that with a nested rewrite approach, it isn’t possible to have multiple placeholders unless the piped expression is pasted multiple times. This would cause multiple evaluations with deleterious effects:</p>
|
||
<div class="sourceCode" id="cb9"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="fu">sample</span>(<span class="dv">10</span>) <span class="sc">%>%</span> <span class="fu">list</span>(., .)</span>
|
||
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Becomes</span></span>
|
||
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="fu">list</span>(<span class="fu">sample</span>(<span class="dv">10</span>), <span class="fu">sample</span>(<span class="dv">10</span>))</span></code></pre></div>
|
||
<p>Assigning to the placeholder within an argument would preserve the nestedness and lazyness. However that wouldn’t work properly because there’s no guarantee that the first argument will be evaluated before the second argument.</p>
|
||
<div class="sourceCode" id="cb10"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="fu">sample</span>(<span class="dv">10</span>) <span class="sc">%>%</span> <span class="fu">foo</span>(., .)</span>
|
||
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="fu">foo</span>(. <span class="ot"><-</span> <span class="fu">sample</span>(<span class="dv">10</span>), .)</span></code></pre></div>
|
||
<p>For these reasons, the nested pipe does not support multiple placeholders. By contrast, all the other variants assign the result of pipe expressions to the placeholder. There are variations in how the placeholder binding is created (lazily or eagerly, in a mask or in the current environment, with numbered symbols or with a unique symbol) but all these variants allow multiple placeholders.</p>
|
||
</div>
|
||
<div id="masking-environment" class="section level4">
|
||
<h4>Masking environment</h4>
|
||
<p>Because the local variants of the pipe evaluate in a mask, they do not have lexical effects or a continuous stack. This is unlike the lexical variants that evaluate in the current environment.</p>
|
||
</div>
|
||
<div id="laziness" class="section level4">
|
||
<h4>Laziness</h4>
|
||
<p>Unlike the lazy variants, all eager versions implementing the pipe with iterated evaluation do not pass the lazy evaluation criterion.</p>
|
||
<p>Secondly, no lazy variant passes the progressive stack criterion. By construction, lazy evaluation requires pushing all the pipe expressions on the stack before evaluation starts. Conversely, all eager variants have a progressive stack.</p>
|
||
</div>
|
||
<div id="numbered-placeholders" class="section level4">
|
||
<h4>Numbered placeholders</h4>
|
||
<p>None of the variants that use numbered placeholders can unbind piped values eagerly. This is how they achieve persistence of these bindings.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="three-implementations" class="section level2">
|
||
<h2>Three implementations</h2>
|
||
<p>The GNU R team is considering implementing the nested approach in base R with a parse-time code transformation (just like <code>-></code> is transformed to <code><-</code> by the parser).</p>
|
||
<p>We have implemented three approaches in magrittr:</p>
|
||
<ul>
|
||
<li>The nested pipe</li>
|
||
<li>The eager lexical pipe</li>
|
||
<li>The lazy masking pipe</li>
|
||
</ul>
|
||
<p>These approaches have complementary strengths and weaknesses.</p>
|
||
<div id="nested-pipe" class="section level3">
|
||
<h3>Nested pipe</h3>
|
||
<div class="sourceCode" id="cb11"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="st">`</span><span class="at">%|>%</span><span class="st">`</span> <span class="ot"><-</span> magrittr<span class="sc">::</span>pipe_nested</span></code></pre></div>
|
||
<div id="multiple-placeholders" class="section level4">
|
||
<h4>Multiple placeholders ❌</h4>
|
||
<p>The nested pipe does not bind expressions to a placeholder and so can’t support multiple placeholders.</p>
|
||
<div class="sourceCode" id="cb12"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="st">"foo"</span> <span class="sc">%|>%</span> <span class="fu">list</span>(., .)</span>
|
||
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="co">#> Error: Can't use multiple placeholders.</span></span></code></pre></div>
|
||
</div>
|
||
<div id="lazy-evaluation" class="section level4">
|
||
<h4>Lazy evaluation ✅</h4>
|
||
<p>Because it relies on the usual rules of argument application, the nested pipe is lazy.</p>
|
||
<div class="sourceCode" id="cb13"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>{</span>
|
||
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">stop</span>(<span class="st">"oh no"</span>) <span class="sc">%|>%</span> <span class="fu">try</span>(<span class="at">silent =</span> <span class="cn">TRUE</span>)</span>
|
||
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> <span class="st">"success"</span></span>
|
||
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>}</span>
|
||
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] "success"</span></span></code></pre></div>
|
||
</div>
|
||
<div id="persistence-and-eager-unbinding" class="section level4">
|
||
<h4>Persistence and eager unbinding ✅</h4>
|
||
<p>The pipe expressions are binded as promises within the execution environment of each function. This environment persists as long as a promise holds onto it. Evaluating the promise discards the reference to the environment which becomes available for garbage collection.</p>
|
||
<p>For instance, here is a function factory that creates a function. The constructed function returns the value supplied at the time of creation:</p>
|
||
<div class="sourceCode" id="cb14"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a>factory <span class="ot"><-</span> <span class="cf">function</span>(x) <span class="cf">function</span>() x</span>
|
||
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>fn <span class="ot"><-</span> <span class="fu">factory</span>(<span class="cn">TRUE</span>)</span>
|
||
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a><span class="fu">fn</span>()</span>
|
||
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] TRUE</span></span></code></pre></div>
|
||
<p>This does not cause any issue with the nested pipe:</p>
|
||
<div class="sourceCode" id="cb15"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>fn <span class="ot"><-</span> <span class="cn">TRUE</span> <span class="sc">%|>%</span> <span class="fu">factory</span>()</span>
|
||
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="fu">fn</span>()</span></code></pre></div>
|
||
</div>
|
||
<div id="progressive-stack" class="section level4">
|
||
<h4>Progressive stack ❌</h4>
|
||
<p>Because the piped expressions are lazily evaluated, the whole pipeline is pushed on the stack before execution starts. This results in a more complex backtrace than necessary:</p>
|
||
<div class="sourceCode" id="cb16"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>faulty <span class="ot"><-</span> <span class="cf">function</span>() <span class="fu">stop</span>(<span class="st">"tilt"</span>)</span>
|
||
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>f <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">+</span> <span class="dv">1</span></span>
|
||
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>g <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">+</span> <span class="dv">2</span></span>
|
||
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>h <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">+</span> <span class="dv">3</span></span>
|
||
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a><span class="fu">faulty</span>() <span class="sc">%|>%</span> <span class="fu">f</span>() <span class="sc">%|>%</span> <span class="fu">g</span>() <span class="sc">%|>%</span> <span class="fu">h</span>()</span>
|
||
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a><span class="co">#> Error in faulty() : tilt</span></span>
|
||
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a><span class="fu">traceback</span>()</span>
|
||
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a><span class="co">#> 7: stop("tilt")</span></span>
|
||
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a><span class="co">#> 6: faulty()</span></span>
|
||
<span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a><span class="co">#> 5: f(faulty())</span></span>
|
||
<span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a><span class="co">#> 4: g(f(faulty()))</span></span>
|
||
<span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a><span class="co">#> 3: h(g(f(faulty())))</span></span>
|
||
<span id="cb16-15"><a href="#cb16-15" aria-hidden="true" tabindex="-1"></a><span class="co">#> 2: .External2(magrittr_pipe) at pipe.R#181</span></span>
|
||
<span id="cb16-16"><a href="#cb16-16" aria-hidden="true" tabindex="-1"></a><span class="co">#> 1: faulty() %|>% f() %|>% g() %|>% h()</span></span></code></pre></div>
|
||
<p>Also note how the expressions in the backtrace look different from the actual code. This is because of the nested rewrite of the pipeline.</p>
|
||
</div>
|
||
<div id="lexical-effects" class="section level4">
|
||
<h4>Lexical effects ✅</h4>
|
||
<p>This is a benefit of using the normal R rules of evaluation. Side effects occur in the correct environment:</p>
|
||
<div class="sourceCode" id="cb17"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>foo <span class="ot"><-</span> <span class="cn">FALSE</span></span>
|
||
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="cn">TRUE</span> <span class="sc">%|>%</span> <span class="fu">assign</span>(<span class="st">"foo"</span>, .)</span>
|
||
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>foo</span>
|
||
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] TRUE</span></span></code></pre></div>
|
||
<p>Control flow has the correct behaviour:</p>
|
||
<div class="sourceCode" id="cb18"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>fn <span class="ot"><-</span> <span class="cf">function</span>() {</span>
|
||
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a> <span class="cn">TRUE</span> <span class="sc">%|>%</span> <span class="fu">return</span>()</span>
|
||
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a> <span class="cn">FALSE</span></span>
|
||
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a>}</span>
|
||
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a><span class="fu">fn</span>()</span>
|
||
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] TRUE</span></span></code></pre></div>
|
||
</div>
|
||
<div id="continuous-stack" class="section level4">
|
||
<h4>Continuous stack ✅</h4>
|
||
<p>Because evaluation occurs in the current environment, the stack is continuous. Let’s instrument errors with a structured backtrace to see what that means:</p>
|
||
<div class="sourceCode" id="cb19"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="fu">options</span>(<span class="at">error =</span> rlang<span class="sc">::</span>entrace)</span></code></pre></div>
|
||
<p>The tree representation of the backtrace correctly represents the hierarchy of execution frames:</p>
|
||
<div class="sourceCode" id="cb20"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a>foobar <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">%|>%</span> <span class="fu">quux</span>()</span>
|
||
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>quux <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">%|>%</span> <span class="fu">stop</span>()</span>
|
||
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a><span class="st">"tilt"</span> <span class="sc">%|>%</span> <span class="fu">foobar</span>()</span>
|
||
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a><span class="co">#> Error in x %|>% stop() : tilt</span></span>
|
||
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a>rlang<span class="sc">::</span><span class="fu">last_trace</span>()</span>
|
||
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a><span class="co">#> <error/rlang_error></span></span>
|
||
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a><span class="co">#> tilt</span></span>
|
||
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a><span class="co">#> Backtrace:</span></span>
|
||
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true" tabindex="-1"></a><span class="co">#> █</span></span>
|
||
<span id="cb20-12"><a href="#cb20-12" aria-hidden="true" tabindex="-1"></a><span class="co">#> 1. ├─"tilt" %|>% foobar()</span></span>
|
||
<span id="cb20-13"><a href="#cb20-13" aria-hidden="true" tabindex="-1"></a><span class="co">#> 2. └─global::foobar("tilt")</span></span>
|
||
<span id="cb20-14"><a href="#cb20-14" aria-hidden="true" tabindex="-1"></a><span class="co">#> 3. ├─x %|>% quux()</span></span>
|
||
<span id="cb20-15"><a href="#cb20-15" aria-hidden="true" tabindex="-1"></a><span class="co">#> 4. └─global::quux(x)</span></span>
|
||
<span id="cb20-16"><a href="#cb20-16" aria-hidden="true" tabindex="-1"></a><span class="co">#> 5. └─x %|>% stop()</span></span></code></pre></div>
|
||
</div>
|
||
</div>
|
||
<div id="eager-lexical-pipe" class="section level3">
|
||
<h3>Eager lexical pipe</h3>
|
||
<div class="sourceCode" id="cb21"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="st">`</span><span class="at">%!>%</span><span class="st">`</span> <span class="ot"><-</span> magrittr<span class="sc">::</span>pipe_eager_lexical</span></code></pre></div>
|
||
<div id="multiple-placeholders-1" class="section level4">
|
||
<h4>Multiple placeholders ✅</h4>
|
||
<p>Pipe expressions are eagerly assigned to the placeholder. This makes it possible to use the placeholder multiple times without causing multiple evaluations.</p>
|
||
<div class="sourceCode" id="cb22"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="st">"foo"</span> <span class="sc">%!>%</span> <span class="fu">list</span>(., .)</span>
|
||
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="co">#> [[1]]</span></span>
|
||
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] "foo"</span></span>
|
||
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a><span class="co">#> </span></span>
|
||
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a><span class="co">#> [[2]]</span></span>
|
||
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] "foo"</span></span></code></pre></div>
|
||
</div>
|
||
<div id="lazy-evaluation-1" class="section level4">
|
||
<h4>Lazy evaluation ❌</h4>
|
||
<p>Assignment forces eager evaluation of each step.</p>
|
||
<div class="sourceCode" id="cb23"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a>{</span>
|
||
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">stop</span>(<span class="st">"oh no"</span>) <span class="sc">%!>%</span> <span class="fu">try</span>(<span class="at">silent =</span> <span class="cn">TRUE</span>)</span>
|
||
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a> <span class="st">"success"</span></span>
|
||
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>}</span>
|
||
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a><span class="co">#> Error in stop("oh no") %!>% try(silent = TRUE): oh no</span></span></code></pre></div>
|
||
</div>
|
||
<div id="persistence" class="section level4">
|
||
<h4>Persistence: ❌</h4>
|
||
<p>Because we’re updating the value of <code>.</code> at each step, the piped expressions are not persistent. This has subtle effects when the piped expressions are not evaluated right away.</p>
|
||
<p>With the eager pipe we get rather confusing results with the factory function if we try to call the constructed function in the middle of the pipeline. In the following snippet the placeholder <code>.</code> is binded to the constructed function itself rather than the initial value <code>TRUE</code>, by the time the function is called:</p>
|
||
<div class="sourceCode" id="cb24"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a>fn <span class="ot"><-</span> <span class="cn">TRUE</span> <span class="sc">%!>%</span> <span class="fu">factory</span>() <span class="sc">%!>%</span> { .() }</span>
|
||
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a><span class="fu">fn</span>()</span>
|
||
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a><span class="co">#> function() x</span></span>
|
||
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a><span class="co">#> <bytecode: 0x11d1b6758></span></span>
|
||
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a><span class="co">#> <environment: 0x11da6d628></span></span></code></pre></div>
|
||
<p>Also, since we’re binding <code>.</code> in the current environment, we need to clean it up once the pipeline has returned. At that point, the placeholder no longer exists:</p>
|
||
<div class="sourceCode" id="cb25"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a>fn <span class="ot"><-</span> <span class="cn">TRUE</span> <span class="sc">%!>%</span> <span class="fu">factory</span>()</span>
|
||
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a><span class="fu">fn</span>()</span>
|
||
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a><span class="co">#> Error in fn(): object '.' not found</span></span></code></pre></div>
|
||
<p>Or it has been reset to its previous value, if any:</p>
|
||
<div class="sourceCode" id="cb26"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a>. <span class="ot"><-</span> <span class="st">"wrong"</span></span>
|
||
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a>fn <span class="ot"><-</span> <span class="cn">TRUE</span> <span class="sc">%!>%</span> <span class="fu">factory</span>()</span>
|
||
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a><span class="fu">fn</span>()</span>
|
||
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] "wrong"</span></span></code></pre></div>
|
||
</div>
|
||
<div id="eager-unbinding" class="section level4">
|
||
<h4>Eager unbinding: ✅</h4>
|
||
<p>This is the flip side of updating the value of the placeholder at each step. The previous intermediary values can be collected right away.</p>
|
||
</div>
|
||
<div id="progressive-stack-1" class="section level4">
|
||
<h4>Progressive stack: ✅</h4>
|
||
<p>Since pipe expressions are evaluated one by one as they come, only the relevant part of the pipeline is on the stack when an error is thrown:</p>
|
||
<div class="sourceCode" id="cb27"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a>faulty <span class="ot"><-</span> <span class="cf">function</span>() <span class="fu">stop</span>(<span class="st">"tilt"</span>)</span>
|
||
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a>f <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">+</span> <span class="dv">1</span></span>
|
||
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a>g <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">+</span> <span class="dv">2</span></span>
|
||
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a>h <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">+</span> <span class="dv">3</span></span>
|
||
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a><span class="fu">faulty</span>() <span class="sc">%!>%</span> <span class="fu">f</span>() <span class="sc">%!>%</span> <span class="fu">g</span>() <span class="sc">%!>%</span> <span class="fu">h</span>()</span>
|
||
<span id="cb27-7"><a href="#cb27-7" aria-hidden="true" tabindex="-1"></a><span class="co">#> Error in faulty() : tilt</span></span>
|
||
<span id="cb27-8"><a href="#cb27-8" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb27-9"><a href="#cb27-9" aria-hidden="true" tabindex="-1"></a><span class="fu">traceback</span>()</span>
|
||
<span id="cb27-10"><a href="#cb27-10" aria-hidden="true" tabindex="-1"></a><span class="co">#> 4: stop("tilt")</span></span>
|
||
<span id="cb27-11"><a href="#cb27-11" aria-hidden="true" tabindex="-1"></a><span class="co">#> 3: faulty()</span></span>
|
||
<span id="cb27-12"><a href="#cb27-12" aria-hidden="true" tabindex="-1"></a><span class="co">#> 2: .External2(magrittr_pipe) at pipe.R#163</span></span>
|
||
<span id="cb27-13"><a href="#cb27-13" aria-hidden="true" tabindex="-1"></a><span class="co">#> 1: faulty() %!>% f() %!>% g() %!>% h()</span></span></code></pre></div>
|
||
</div>
|
||
<div id="lexical-effects-and-continuous-stack" class="section level4">
|
||
<h4>Lexical effects and continuous stack: ✅</h4>
|
||
<p>Evaluating in the current environment rather than in a mask produces the correct side effects:</p>
|
||
<div class="sourceCode" id="cb28"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a>foo <span class="ot"><-</span> <span class="cn">FALSE</span></span>
|
||
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a><span class="cn">NA</span> <span class="sc">%!>%</span> { foo <span class="ot"><-</span> <span class="cn">TRUE</span>; . }</span>
|
||
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] NA</span></span>
|
||
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a>foo</span>
|
||
<span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] TRUE</span></span></code></pre></div>
|
||
<div class="sourceCode" id="cb29"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>fn <span class="ot"><-</span> <span class="cf">function</span>() {</span>
|
||
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a> <span class="cn">TRUE</span> <span class="sc">%!>%</span> <span class="fu">return</span>()</span>
|
||
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a> <span class="cn">FALSE</span></span>
|
||
<span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a>}</span>
|
||
<span id="cb29-6"><a href="#cb29-6" aria-hidden="true" tabindex="-1"></a><span class="fu">fn</span>()</span>
|
||
<span id="cb29-7"><a href="#cb29-7" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] TRUE</span></span></code></pre></div>
|
||
</div>
|
||
</div>
|
||
<div id="lazy-masking-pipe" class="section level3">
|
||
<h3>Lazy masking pipe</h3>
|
||
<div class="sourceCode" id="cb30"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="st">`</span><span class="at">%?>%</span><span class="st">`</span> <span class="ot"><-</span> magrittr<span class="sc">::</span>pipe_lazy_masking</span></code></pre></div>
|
||
<div id="multiple-placeholders-2" class="section level4">
|
||
<h4>Multiple placeholders ✅</h4>
|
||
<p>Pipe expressions are lazily assigned to the placeholder. This makes it possible to use the placeholder multiple times without causing multiple evaluations.</p>
|
||
<div class="sourceCode" id="cb31"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="st">"foo"</span> <span class="sc">%?>%</span> <span class="fu">list</span>(., .)</span>
|
||
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a><span class="co">#> [[1]]</span></span>
|
||
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] "foo"</span></span>
|
||
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a><span class="co">#> </span></span>
|
||
<span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a><span class="co">#> [[2]]</span></span>
|
||
<span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] "foo"</span></span></code></pre></div>
|
||
</div>
|
||
<div id="lazy-evaluation-2" class="section level4">
|
||
<h4>Lazy evaluation ✅</h4>
|
||
<p>Arguments are assigned with <code>delayedAssign()</code> and lazily evaluated:</p>
|
||
<div class="sourceCode" id="cb32"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a>{</span>
|
||
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">stop</span>(<span class="st">"oh no"</span>) <span class="sc">%?>%</span> <span class="fu">try</span>(<span class="at">silent =</span> <span class="cn">TRUE</span>)</span>
|
||
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a> <span class="st">"success"</span></span>
|
||
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a>}</span>
|
||
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] "success"</span></span></code></pre></div>
|
||
</div>
|
||
<div id="persistence-1" class="section level4">
|
||
<h4>Persistence: ✅</h4>
|
||
<p>The lazy masking pipe uses one masking environment per pipe expression. This allows persistence of the intermediary values and out of order evaluation. The factory function works as expected for instance:</p>
|
||
<div class="sourceCode" id="cb33"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a>fn <span class="ot"><-</span> <span class="cn">TRUE</span> <span class="sc">%?>%</span> <span class="fu">factory</span>()</span>
|
||
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a><span class="fu">fn</span>()</span>
|
||
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] TRUE</span></span></code></pre></div>
|
||
</div>
|
||
<div id="eager-unbinding-1" class="section level4">
|
||
<h4>Eager unbinding: ✅</h4>
|
||
<p>Because we use one mask environment per pipe expression, the intermediary values can be collected as soon as they are no longer needed.</p>
|
||
</div>
|
||
<div id="progressive-stack-2" class="section level4">
|
||
<h4>Progressive stack: ❌</h4>
|
||
<p>With a lazy pipe the whole pipeline is pushed onto the stack before evaluation.</p>
|
||
<div class="sourceCode" id="cb34"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a>faulty <span class="ot"><-</span> <span class="cf">function</span>() <span class="fu">stop</span>(<span class="st">"tilt"</span>)</span>
|
||
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a>f <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">+</span> <span class="dv">1</span></span>
|
||
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a>g <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">+</span> <span class="dv">2</span></span>
|
||
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a>h <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">+</span> <span class="dv">3</span></span>
|
||
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb34-6"><a href="#cb34-6" aria-hidden="true" tabindex="-1"></a><span class="fu">faulty</span>() <span class="sc">%?>%</span> <span class="fu">f</span>() <span class="sc">%?>%</span> <span class="fu">g</span>() <span class="sc">%?>%</span> <span class="fu">h</span>()</span>
|
||
<span id="cb34-7"><a href="#cb34-7" aria-hidden="true" tabindex="-1"></a><span class="co">#> Error in faulty() : tilt</span></span>
|
||
<span id="cb34-8"><a href="#cb34-8" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb34-9"><a href="#cb34-9" aria-hidden="true" tabindex="-1"></a><span class="fu">traceback</span>()</span>
|
||
<span id="cb34-10"><a href="#cb34-10" aria-hidden="true" tabindex="-1"></a><span class="co">#> 7: stop("tilt")</span></span>
|
||
<span id="cb34-11"><a href="#cb34-11" aria-hidden="true" tabindex="-1"></a><span class="co">#> 6: faulty()</span></span>
|
||
<span id="cb34-12"><a href="#cb34-12" aria-hidden="true" tabindex="-1"></a><span class="co">#> 5: f(.)</span></span>
|
||
<span id="cb34-13"><a href="#cb34-13" aria-hidden="true" tabindex="-1"></a><span class="co">#> 4: g(.)</span></span>
|
||
<span id="cb34-14"><a href="#cb34-14" aria-hidden="true" tabindex="-1"></a><span class="co">#> 3: h(.)</span></span>
|
||
<span id="cb34-15"><a href="#cb34-15" aria-hidden="true" tabindex="-1"></a><span class="co">#> 2: .External2(magrittr_pipe) at pipe.R#174</span></span>
|
||
<span id="cb34-16"><a href="#cb34-16" aria-hidden="true" tabindex="-1"></a><span class="co">#> 1: faulty() %?>% f() %?>% g() %?>% h()</span></span></code></pre></div>
|
||
<p>Note however how the backtrace is less cluttered than with the nested pipe approach, thanks to the placeholder.</p>
|
||
</div>
|
||
<div id="lexical-effects-1" class="section level4">
|
||
<h4>Lexical effects ❌</h4>
|
||
<p>The lazy pipe evaluates in a mask. This causes lexical side effects to occur in the incorrect environment.</p>
|
||
<div class="sourceCode" id="cb35"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a>foo <span class="ot"><-</span> <span class="cn">FALSE</span></span>
|
||
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a><span class="cn">TRUE</span> <span class="sc">%?>%</span> <span class="fu">assign</span>(<span class="st">"foo"</span>, .)</span>
|
||
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a>foo</span>
|
||
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] FALSE</span></span></code></pre></div>
|
||
<p>Stack-sensitive functions like <code>return()</code> function cannot find the proper frame environment:</p>
|
||
<div class="sourceCode" id="cb36"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a>fn <span class="ot"><-</span> <span class="cf">function</span>() {</span>
|
||
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a> <span class="cn">TRUE</span> <span class="sc">%?>%</span> <span class="fu">return</span>()</span>
|
||
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a> <span class="cn">FALSE</span></span>
|
||
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a>}</span>
|
||
<span id="cb36-5"><a href="#cb36-5" aria-hidden="true" tabindex="-1"></a><span class="fu">fn</span>()</span>
|
||
<span id="cb36-6"><a href="#cb36-6" aria-hidden="true" tabindex="-1"></a><span class="co">#> [1] FALSE</span></span></code></pre></div>
|
||
</div>
|
||
<div id="continuous-stack-1" class="section level4">
|
||
<h4>Continuous stack ❌</h4>
|
||
<p>The masking environment causes a discontinuous stack tree:</p>
|
||
<div class="sourceCode" id="cb37"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a>foobar <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">%?>%</span> <span class="fu">quux</span>()</span>
|
||
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a>quux <span class="ot"><-</span> <span class="cf">function</span>(x) x <span class="sc">%?>%</span> <span class="fu">stop</span>()</span>
|
||
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb37-4"><a href="#cb37-4" aria-hidden="true" tabindex="-1"></a><span class="st">"tilt"</span> <span class="sc">%?>%</span> <span class="fu">foobar</span>()</span>
|
||
<span id="cb37-5"><a href="#cb37-5" aria-hidden="true" tabindex="-1"></a><span class="co">#> Error in x %?>% stop() : tilt</span></span>
|
||
<span id="cb37-6"><a href="#cb37-6" aria-hidden="true" tabindex="-1"></a></span>
|
||
<span id="cb37-7"><a href="#cb37-7" aria-hidden="true" tabindex="-1"></a>rlang<span class="sc">::</span><span class="fu">last_trace</span>()</span>
|
||
<span id="cb37-8"><a href="#cb37-8" aria-hidden="true" tabindex="-1"></a><span class="co">#> <error/rlang_error></span></span>
|
||
<span id="cb37-9"><a href="#cb37-9" aria-hidden="true" tabindex="-1"></a><span class="co">#> tilt</span></span>
|
||
<span id="cb37-10"><a href="#cb37-10" aria-hidden="true" tabindex="-1"></a><span class="co">#> Backtrace:</span></span>
|
||
<span id="cb37-11"><a href="#cb37-11" aria-hidden="true" tabindex="-1"></a><span class="co">#> █</span></span>
|
||
<span id="cb37-12"><a href="#cb37-12" aria-hidden="true" tabindex="-1"></a><span class="co">#> 1. ├─"tilt" %?>% foobar()</span></span>
|
||
<span id="cb37-13"><a href="#cb37-13" aria-hidden="true" tabindex="-1"></a><span class="co">#> 2. ├─global::foobar(.)</span></span>
|
||
<span id="cb37-14"><a href="#cb37-14" aria-hidden="true" tabindex="-1"></a><span class="co">#> 3. │ └─x %?>% quux()</span></span>
|
||
<span id="cb37-15"><a href="#cb37-15" aria-hidden="true" tabindex="-1"></a><span class="co">#> 4. └─global::quux(.)</span></span>
|
||
<span id="cb37-16"><a href="#cb37-16" aria-hidden="true" tabindex="-1"></a><span class="co">#> 5. └─x %?>% stop()</span></span></code></pre></div>
|
||
</div>
|
||
</div>
|
||
</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>
|