JavaScript cont in R

[This article was first published on Colin Fay, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

One thing I like about JavaScript is the const declaration method,
which allows you to declare a variable one time, and that variable can’t
be reassigned after that. I.e, this piece of code will throw an error:

node <span class="nt">-e</span> <span class="s2">"const x = 12; x  = 14"</span>
## [eval]:1
## const x = 12; x  = 14
##                  ^
## 
## TypeError: Assignment to constant variable.
##     at [eval]:1:18
##     at Script.runInThisContext (vm.js:124:20)
##     at Object.runInThisContext (vm.js:314:38)
##     at Object.<anonymous> ([eval]-wrapper:9:26)
##     at Module._compile (internal/modules/cjs/loader.js:805:30)
##     at evalScript (internal/process/execution.js:60:25)
##     at internal/main/eval_string.js:16:1

The cool thing about this is that you can’t override the variable by
mistake: once it’s set, it’s set. On the other hand, R allows you to
override almost any variable (well, except some reserved variables).

I asked Twitter if there was any implementation of that concept in R.
The use case, for example, would arise when you have a value that takes
some time to compute. If I do my computation, I can accidentally
override it later on. Event more if you’re using notebook, where you
create symbols and values all along your document.

<span class="n">a</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">some_very_complex_computation</span><span class="p">()</span><span class="w">
</span><span class="c1"># [...] Going on the weekend</span><span class="w">
</span><span class="n">a</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="s2">"Hello there!"</span><span class="w">
</span>

Here, I have no way to prevent myself from erasing the value in a. Of
course, there are always rigor, explicit variable name, and
not-assigning-things-without-thinking but you know how it is in the real
world, and there is no Cmd + Z there.

Romain
pointed out that ?lockBinding existed, and that it was what I was
looking for. And that does.

Here’s how it works: it takes a character string referring to a symbol,
and an environment, and prevents from assigning any new value to this
symbol in the given environment.

<span class="n">x</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="m">12</span><span class="w">
</span><span class="n">lockBinding</span><span class="p">(</span><span class="s2">"x"</span><span class="p">,</span><span class="w"> </span><span class="n">.GlobalEnv</span><span class="p">)</span><span class="w">
</span><span class="n">x</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="m">13</span><span class="w">
</span>
## Error in eval(expr, envir, enclos): cannot change value of locked binding for 'x'

And here’s a small wrapper to do that:

<span class="n">lock</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">){</span><span class="w">
  </span><span class="n">lockBinding</span><span class="p">(</span><span class="w">
    </span><span class="n">deparse</span><span class="p">(</span><span class="w">
      </span><span class="nf">substitute</span><span class="p">(</span><span class="n">x</span><span class="p">)),</span><span class="w"> 
    </span><span class="n">env</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">parent.frame</span><span class="p">()</span><span class="w">
  </span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">plop</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="m">12</span><span class="w">
</span><span class="n">lock</span><span class="p">(</span><span class="n">plop</span><span class="p">)</span><span class="w">
</span><span class="n">plop</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="m">13</span><span class="w">
</span>
## Error in eval(expr, envir, enclos): cannot change value of locked binding for 'plop'
<span class="n">pouet</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(){</span><span class="w">
  </span><span class="n">plop</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="m">14</span><span class="w">
  </span><span class="n">print</span><span class="p">(</span><span class="n">plop</span><span class="p">)</span><span class="w">
  </span><span class="n">lock</span><span class="p">(</span><span class="n">plop</span><span class="p">)</span><span class="w">
  </span><span class="n">plop</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="m">13</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">pouet</span><span class="p">()</span><span class="w">
</span>
## [1] 14

## Error in pouet(): cannot change value of locked binding for 'plop'

So there I could do

<span class="n">a</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">some_very_complex_computation</span><span class="p">()</span><span class="w">
</span><span class="n">lock</span><span class="p">(</span><span class="n">a</span><span class="p">)</span><span class="w">
</span><span class="c1"># [...] Going on the weekend</span><span class="w">
</span><span class="n">a</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="s2">"Hello there!"</span><span class="w">
</span>

And there, I have prevented myself from erasing my a variable. Of
course, it’s not the same as JavaScript const, as there is always a
way to unlock the
symbol.

<span class="n">x</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="m">12</span><span class="w">
</span>
## Error in eval(expr, envir, enclos): cannot change value of locked binding for 'x'
<span class="n">lock</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
</span><span class="n">x</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="m">13</span><span class="w">
</span>
## Error in eval(expr, envir, enclos): cannot change value of locked binding for 'x'
<span class="n">unlockBinding</span><span class="p">(</span><span class="s2">"x"</span><span class="p">,</span><span class="w"> </span><span class="n">.GlobalEnv</span><span class="p">)</span><span class="w">
</span><span class="n">x</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="m">13</span><span class="w">
</span><span class="n">x</span><span class="w">
</span>
## [1] 13

But I think it’s a rather elegant solution for preventing yourself from
unwanted variable overwriting.

See also:

Some answers to the Twitter thread also suggested using R6… but that
will be for another post 🙂

To leave a comment for the author, please follow the link and comment on their blog: Colin Fay.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)