<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/wrapr-implementation-update/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="http://www.win-vector.com/blog/2017/06/wrapr-implementation-update/"> R – Win-Vector Blog</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<h2>Introduction</h2>
<p>The <a rel="nofollow" href="https://github.com/WinVector/wrapr" target="_blank">development version</a> of our <a rel="nofollow" href="https://cran.r-project.org/" target="_blank"><code>R</code></a> helper function <code>wrapr::let()</code> has switched from string-based substitution to abstract syntax tree based substitution (AST based subsitution, or language based substitution).</p>
<p><center><img border="0" alt="Wraprs" title="wraprs.png" src="https://i2.wp.com/www.win-vector.com/blog/wp-content/uploads/2017/06/wraprs.png?w=660" data-recalc-dims="1"></center></p>
<p>I am looking for some feedback from <code>wrapr::let()</code> users already doing substantial work with <code>wrapr::let()</code>. If you are already using <code>wrapr::let()</code> please test if the current <a rel="nofollow" href="https://github.com/WinVector/wrapr" target="_blank">development version of <code>wrapr</code></a> works with your code. If you run into problems: I apologize, and please file a <code>GitHub</code> issue.</p>
<p><span></span></p>
<h2>The substitution modes</h2>
<p>The development version of <code>wrapr::let()</code> now has three substitution implementations:</p>
<ul>
<li>Language substitution (<code>subsMethod='langsubs'</code> the new default). In this mode user code is captured as an abstract syntax tree (or parse tree) and substitution is performed only on nodes known to be symbols.</li>
<li>String substitution (<code>subsMethod='stringsubs'</code>, the CRAN current default). In this mode user code is captured as text and then string replacement on word-boundaries is used to substitute in variable re-mappings.</li>
<li>Substitute substitution (<code>subsMethod='subsubs'</code>, new to the development version). In this mode substitution is performed by <code>R</code>‘s own <code>base::substitute()</code>.</li>
</ul>
<p>The semantics of the three methods can be illustrated by showing the effects of substituting the variable name &quot;<code>y</code>&quot; for &quot;<code>X</code>&quot; in the somewhat complicated block of statements:</p>
<div>
<pre><code>  {
    d &lt;-<span> </span><span>data.frame</span>(<span>&quot;X&quot;</span> =<span> &quot;X&quot;</span>, <span>X2 =</span> <span>&quot;XX&quot;</span>, <span>d =</span> X*X, <span>.X =</span> X_)
    X &lt;-<span> </span><span>list</span>(<span>X =</span> d$X, <span>X2 =</span> d$<span>&quot;X&quot;</span>, <span>v1 =</span> <span>`</span><span>X</span><span>`</span>, <span>v2 =</span> <span>`</span><span> X</span><span>`</span>)
  }</code></pre>
</div>
<p>This block a lot of different examples and corner-cases.</p>
<h4>Language substitution (<code>subsMethod='langsubs'</code>)</h4>
<div>
<pre><code><span>library</span>(<span>&quot;wrapr&quot;</span>)

<span>let</span>(
  <span>c</span>(<span>X =</span> <span>'y'</span>), 
  {
    d &lt;-<span> </span><span>data.frame</span>(<span>&quot;X&quot;</span> =<span> &quot;X&quot;</span>, <span>X2 =</span> <span>&quot;XX&quot;</span>, <span>d =</span> X*X, <span>.X =</span> X_)
    X &lt;-<span> </span><span>list</span>(<span>X =</span> d$X, <span>X2 =</span> d$<span>&quot;X&quot;</span>, <span>v1 =</span> <span>`</span><span>X</span><span>`</span>, <span>v2 =</span> <span>`</span><span> X</span><span>`</span>)
  },
  <span>eval =</span> <span>FALSE</span>, <span>subsMethod =</span> <span>'langsubs'</span>)</code></pre>
</div>
<pre><code>## {
##     d &lt;- data.frame(y = &quot;X&quot;, X2 = &quot;XX&quot;, d = y * y, .X = X_)
##     y &lt;- list(y = d$y, X2 = d$y, v1 = y, v2 = ` X`)
## }</code></pre>
<p>Notice the substitution replaced all symbol-like uses of &quot;<code>X</code>&quot;, and only these (including some that were quoted!).</p>
<p>The reason I need more testing on this method is <code>R</code> language structures are fairly complicated, so there may be some corner cases we want additional coding for. For example treating <code>d$&quot;X&quot;</code> the same as <code>d$X</code> is in fact handled by some special-case code.</p>
<h4>String substitution (<code>subsMethod='stringsubs'</code>)</h4>
<div>
<pre><code><span>let</span>(
  <span>c</span>(<span>X =</span> <span>'y'</span>), 
  {
    d &lt;-<span> </span><span>data.frame</span>(<span>&quot;X&quot;</span> =<span> &quot;X&quot;</span>, <span>X2 =</span> <span>&quot;XX&quot;</span>, <span>d =</span> X*X, <span>.X =</span> X_)
    X &lt;-<span> </span><span>list</span>(<span>X =</span> d$X, <span>X2 =</span> d$<span>&quot;X&quot;</span>, <span>v1 =</span> <span>`</span><span>X</span><span>`</span>, <span>v2 =</span> <span>`</span><span> X</span><span>`</span>)
  },
  <span>eval =</span> <span>FALSE</span>, <span>subsMethod =</span> <span>'stringsubs'</span>)</code></pre>
</div>
<pre><code>## expression({
##     d &lt;- data.frame(y = &quot;y&quot;, X2 = &quot;XX&quot;, d = y * y, .y = X_)
##     y &lt;- list(y = d$y, X2 = d$y, v1 = y, v2 = ` y`)
## })</code></pre>
<p>Notice string substitution has a few flaws: it went after variable names that appeared to start with a word-boundary (the cases where the variable name started with a dot or a space). Substitution also occurred in some string constants (which as we have seen could be considered a good thing).</p>
<p>These situations are all avoidable as both the code inside the <code>let</code>-block and the substitution targets are chosen by the programmer, so they can be chosen to be simple and mutually consisitent. We suggest &quot;<code>ALL_CAPS</code>&quot; style substitution targets as they jump out as being macro targets. But, of course, it is better to have stricter control on substitution.</p>
<p>Think of the language substitution implementation as a lower-bound on a perfect implementation (cautious, with a few corner cases to get coverage) and string substitution as an upper bound on a perfect implementation (aggressive, with a few over-reaches).</p>
<h4>Substitute substitution (<code>subsMethod='subsubs'</code>)</h4>
<div>
<pre><code><span>let</span>(<span>c</span>(<span>X =</span> <span>'y'</span>), 
    {
      d &lt;-<span> </span><span>data.frame</span>(<span>&quot;X&quot;</span> =<span> &quot;X&quot;</span>, <span>X2 =</span> <span>&quot;XX&quot;</span>, <span>d =</span> X*X, <span>.X =</span> X_)
      X &lt;-<span> </span><span>list</span>(<span>X =</span> d$X, <span>X2 =</span> d$<span>&quot;X&quot;</span>, <span>v1 =</span> <span>`</span><span>X</span><span>`</span>, <span>v2 =</span> <span>`</span><span> X</span><span>`</span>)
    },
    <span>eval =</span> <span>FALSE</span>, <span>subsMethod =</span> <span>'subsubs'</span>)</code></pre>
</div>
<pre><code>## {
##     d &lt;- data.frame(X = &quot;X&quot;, X2 = &quot;XX&quot;, d = y * y, .X = X_)
##     y &lt;- list(X = d$y, X2 = d$X, v1 = y, v2 = ` X`)
## }</code></pre>
<p>Notice <code>base::substitute()</code> doesn’t re-write left-hand-sides of argument bindings. This is why I originally didn’t consider using this implementation. Re-writing left-hand-sides of assignments is critical in expressions such as <code>dplyr::mutate( RESULTCOL = INPUTCOL + 1)</code>. Also <code>base::substitute()</code> doesn’t special case the <code>d$&quot;X&quot;</code> situation (which really isn’t that important).</p>
<h2>Conclusion</h2>
<p><code>wrapr::let()</code> when used prudently is already a safe and powerful tool. That being said it will be improved by switching to abstract syntax tree based substitution. I’d love to do that in the next release, so it would helpful to collect more experience using the current development version prior to that.</p>
<h2>Appendix <code>wrapr::let()</code> for beginners</h2>
<p>Obviously <code>wrapr::let()</code> is not interesting if you have no idea why you would need it or how to use it.</p>
<p><code>wrapr::let()</code> is designed to solve a single problem when programming in <code>R</code>: when in writing code you don’t yet know the name of a variable or a <code>data.frame</code> column you are going to use. That is: the name of the column will only be available later when your code is run.</p>
<p>For example suppose we have a <code>data.frame</code> &quot;<code>d</code>&quot; defined as follows:</p>
<div>
<pre><code>d &lt;-<span> </span><span>data.frame</span>(<span>x =</span> <span>c</span>(<span>15</span>, <span>30</span>, <span>40</span>))
<span>print</span>( d )</code></pre>
</div>
<pre><code>##    x
## 1 15
## 2 30
## 3 40</code></pre>
<p>If we know the name of the column we can access it as follows:</p>
<div>
<pre><code><span>print</span>( d$x )</code></pre>
</div>
<pre><code>## [1] 15 30 40</code></pre>
<p>If we don’t know the name of the column (such as would be the case when writing a function) we write code like the following:</p>
<div>
<pre><code>getColumn &lt;-<span> </span>function(df, columnName) {
  df[[columnName]]
}

<span>print</span>( <span>getColumn</span>(d, <span>'x'</span>) )</code></pre>
</div>
<pre><code>## [1] 15 30 40</code></pre>
<p>This works because <code>R</code> takes a lot of trouble to supply parametric interfaces for most use cases.</p>
<p>We only run into trouble if code we are trying to work with strongly prefers non-parametric (also called non-standard-evaluation or NSE) interfaces.</p>
<p>A popular package that heavily emphasizes NSE interfaces is <code>dplyr</code>. It is the case that <code>dplyr</code> supplies its own methods to work around NSE issues (&quot;underbar methods&quot; and <code>lazyeval</code> for <code>dplyr</code> <code>0.5.0</code>, and <code>rlang</code>/<code>tidyeval</code> for <code>dplyr</code> <code>0.7.0</code>). However, we will only discuss <code>wrapr::let()</code> methodology here.</p>
<p>The issue is: it is common to write statements such as the following in <code>dplyr</code>:</p>
<div>
<pre><code><span>suppressPackageStartupMessages</span>(<span>library</span>(<span>&quot;dplyr&quot;</span>))

d %>%<span> </span><span>mutate</span>( <span>v2 =</span> x +<span> </span><span>1</span> )</code></pre>
</div>
<pre><code>##    x v2
## 1 15 16
## 2 30 31
## 3 40 41</code></pre>
<p>If we were writing the above in a function it would plausible that we would not know the name of the desired result column or the name of the column to add one to. <code>wrapr::let()</code> lets us write such code easily:</p>
<div>
<pre><code><span>library</span>(<span>&quot;wrapr&quot;</span>)

addOneToColumn &lt;-<span> </span>function(df, 
                           result_column_name, 
                           input_column_name) {
  <span>let</span>(
    <span>c</span>(<span>RESULT_COL =</span> result_column_name,
      <span>INPUT_COL =</span> input_column_name),
    
    df %>%<span> </span><span>mutate</span>( <span>RESULT_COL =</span> INPUT_COL +<span> </span><span>1</span> )
    
  )
}

d %>%<span> </span><span>addOneToColumn</span>(<span>'v2'</span>, <span>'x'</span>)</code></pre>
</div>
<pre><code>##    x v2
## 1 15 16
## 2 30 31
## 3 40 41</code></pre>
<p>Again, writing the function <code>addOneToColumn()</code> was the goal. The issue was that in such a function we don’t know what column names the user is going to end up supplying. We work around this difficulty with <code>wrapr::let()</code></p>
<p>All the user needs to keep in mind is: <code>wrapr::let()</code> takes two primary arguments:</p>
<ul>
<li>The aliases mapping concrete stand-in names (here shown in <code>ALL_CAPS</code> style) to the variables holding the actual variable names.</li>
<li>The block of code to re-write and execute (which can be a single statement, or a larger block with braces).</li>
</ul>
	<p>
<div>
<div>To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="http://www.win-vector.com/blog/2017/06/wrapr-implementation-update/"> R – Win-Vector Blog</a></strong>.</div>
<hr>
<a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers.com</a> offers <strong><a rel="nofollow" href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers">daily e-mail updates</a></strong> about <a rel="nofollow" href="https://www.r-project.org/" title="The R Project for Statistical Computing">R</a> news and <a rel="nofollow" href="https://www.r-bloggers.com/search/tutorial" title="R tutorials">tutorials</a> on topics such as: <a rel="nofollow" href="https://www.r-bloggers.com/search/data%20science" title="Data science">Data science</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Big%20Data" title="Big Data">Big Data, <a rel="nofollow" href="https://www.r-users.com/" title="R jobs">R jobs</a>, visualization (<a rel="nofollow" href="https://www.r-bloggers.com/search/ggplot2" title="ggplot and ggplot2 tutorials">ggplot2</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/boxplot" title="Boxplots using lattice and ggplot2 tutorials">Boxplots</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/map" title="Maps and gis">maps</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/animation" title="Animation in R">animation</a>), programming (<a rel="nofollow" href="https://www.r-bloggers.com/search/RStudio" title="RStudio IDE for R">RStudio</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/sweave" title="Sweave and literate programming">Sweave</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/LaTeX" title="LaTeX in R">LaTeX</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/SQL" title="SQL and databases">SQL</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/eclipse" title="Eclipse IDE for R">Eclipse</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/git" title="git and github, Version Control System">git</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/hadoop" title="Large data in R using Hadoop">hadoop</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Web+Scraping" title="Web Scraping of google, facebook, yahoo, twitter and more using R">Web Scraping</a>) statistics (<a rel="nofollow" href="https://www.r-bloggers.com/search/regression" title="Regressions and ANOVA analysis tutorials">regression</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/PCA" title="principal component analysis tutorial">PCA</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/time+series" title="Time series">time series</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/trading" title="finance trading">trading</a>) and more...
<img width="1" alt="" src="http://feeds.feedburner.com/~r/RBloggers/~4/OibRES_8K4c" height="1"></a></div>