{attempt} 0.3.0 is now on CRAN

[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.

Last week, a new version of {attempt} was published on CRAN. This
version includes some improvements in the current code base, and the
addition of new functions.

You can get it with our old friend install.packages

<span class="n">install.packages</span><span class="p">(</span><span class="s2">"attempt"</span><span class="p">)</span><span class="w">
</span>

News in version 0.3.0

<span class="n">library</span><span class="p">(</span><span class="n">attempt</span><span class="p">)</span><span class="w">
</span><span class="n">packageVersion</span><span class="p">(</span><span class="s2">"attempt"</span><span class="p">)</span><span class="w">
</span>
## [1] '0.3.0'

Newcomers in this version:

  • The is_try_error() function, that tests if an object is of class
    “try-error”.

<span class="n">x</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">attempt</span><span class="p">(</span><span class="nf">log</span><span class="p">(</span><span class="nb">letters</span><span class="p">),</span><span class="w"> </span><span class="n">silent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
</span><span class="n">is_try_error</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
</span>
## [1] TRUE
  • The on_error() function, that behaves as on.exit() except it
    happens only when there is an error in the function, and can for
    example be used to write logs to a file.

<span class="n">y</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">on_error</span><span class="p">(</span><span class="o">~</span><span class="w"> </span><span class="n">print</span><span class="p">(</span><span class="s2">"ouch"</span><span class="p">))</span><span class="w"> </span><span class="c1"># or on_error(~ write("error", "error.logs"))</span><span class="w">
  </span><span class="nf">log</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">y</span><span class="p">(</span><span class="m">12</span><span class="p">)</span><span class="w">
</span><span class="n">y</span><span class="p">(</span><span class="s2">"a"</span><span class="p">)</span><span class="w">

</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="m">2.484907</span><span class="w">
</span><span class="n">Error</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nf">log</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="n">non</span><span class="o">-</span><span class="n">numeric</span><span class="w"> </span><span class="n">argument</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">mathematical</span><span class="w"> </span><span class="k">function</span><span class="w">
</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="s2">"ouch"</span><span class="w">
</span>
  • discretly(), for removing warnings and message.

<span class="n">y</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">warning</span><span class="p">(</span><span class="s2">"ouch\n"</span><span class="p">)</span><span class="w">
  </span><span class="n">message</span><span class="p">(</span><span class="s2">"bla"</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">10</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">y</span><span class="p">(</span><span class="m">10</span><span class="p">)</span><span class="w">
</span>
## Warning in y(10): ouch

## bla

## [1] 100
<span class="n">discrete_y</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">discretly</span><span class="p">(</span><span class="n">y</span><span class="p">)</span><span class="w">
</span><span class="n">discrete_y</span><span class="p">(</span><span class="m">10</span><span class="p">)</span><span class="w">
</span>
## [1] 100

About {attempt}

{attempt} is a package that provides a series of tools for defensive
programming. It’s mainly designed for (package) developers, as it
provides easier ways to handle exceptions and to manipulate functions
for messages, warnings and errors. It’s also a very lightweight package,
as it only depends on {rlang}, which itself has no dependencies.

For example, let’s say you have a function that needs at least one
argument to be not-null. You would be tempted to write something like
this:

<span class="n">this</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">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">){</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">all</span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">a</span><span class="p">),</span><span class="w"> </span><span class="nf">is.null</span><span class="p">(</span><span class="n">b</span><span class="p">),</span><span class="w"> </span><span class="nf">is.null</span><span class="p">(</span><span class="n">c</span><span class="p">))){</span><span class="w">
    </span><span class="n">stop</span><span class="p">(</span><span class="s2">"a, b and c can't be all NULL"</span><span class="p">)</span><span class="w">
  </span><span class="p">}</span><span class="w">
  </span><span class="nf">list</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">this</span><span class="p">()</span><span class="w">
</span>
## Error in this(): a, b and c can't be all NULL
<span class="c1"># Or</span><span class="w">
</span><span class="n">this</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">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">){</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">all</span><span class="p">(</span><span class="n">vapply</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">),</span><span class="w"> </span><span class="n">is.null</span><span class="p">,</span><span class="w"> </span><span class="n">logical</span><span class="p">(</span><span class="m">1</span><span class="p">)))){</span><span class="w">
    </span><span class="n">stop</span><span class="p">(</span><span class="s2">"a, b and c can't be all NULL"</span><span class="p">)</span><span class="w">
  </span><span class="p">}</span><span class="w">
  </span><span class="nf">list</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">this</span><span class="p">()</span><span class="w">
</span>
## Error in this(): a, b and c can't be all NULL
<span class="n">this</span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">
</span>
## [[1]]
## [1] 1
## 
## [[2]]
## NULL
## 
## [[3]]
## NULL

With {attempt}, you can refactor your code this way:

<span class="n">library</span><span class="p">(</span><span class="n">attempt</span><span class="p">)</span><span class="w">
</span><span class="n">this</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">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">){</span><span class="w">
  </span><span class="n">stop_if_all</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">),</span><span class="w"> </span><span class="n">is.null</span><span class="p">,</span><span class="w"> </span><span class="s2">"a, b and c can't be all NULL"</span><span class="p">)</span><span class="w"> 
  </span><span class="nf">list</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">this</span><span class="p">()</span><span class="w">
</span>
## Error: a, b and c can't be all NULL
<span class="c1"># Mappers can also be used</span><span class="w">
</span><span class="n">this</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">a</span><span class="p">){</span><span class="w">
  </span><span class="n">stop_if</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="o">~</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">5</span><span class="p">,</span><span class="w"> </span><span class="s2">"a must be over 5"</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">this</span><span class="p">(</span><span class="m">1</span><span class="p">)</span><span class="w">
</span>
## Error: a must be over 5

To handle all cases, there is a series of function combining stop_if /
warn_if / message_if & all, any and none. See ?stop_if for a
list of all functions.

The idea being of course that if you have a series of tests, this can
drastically reduce the amount of code at the beginning of the function,
and make it more readable on the long run. So you can refactor this:

<span class="n">this</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">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">){</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">all</span><span class="p">(</span><span class="nf">is.null</span><span class="p">(</span><span class="n">a</span><span class="p">),</span><span class="w"> </span><span class="nf">is.null</span><span class="p">(</span><span class="n">b</span><span class="p">),</span><span class="w"> </span><span class="nf">is.null</span><span class="p">(</span><span class="n">c</span><span class="p">))){</span><span class="w">
    </span><span class="n">stop</span><span class="p">(</span><span class="s2">"a, b and c can't be all NULL\n"</span><span class="p">)</span><span class="w">
  </span><span class="p">}</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">any</span><span class="p">(</span><span class="n">vapply</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">),</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">x</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="m">5</span><span class="p">,</span><span class="w"> </span><span class="n">logical</span><span class="p">(</span><span class="m">1</span><span class="p">)))){</span><span class="w">
    </span><span class="n">warning</span><span class="p">(</span><span class="s2">"using input below 5 is not recommended\n"</span><span class="p">)</span><span class="w">
  </span><span class="p">}</span><span class="w">
  </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nf">any</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="m">13</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="m">13</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="m">13</span><span class="p">))){</span><span class="w">
    </span><span class="n">message</span><span class="p">(</span><span class="s2">"No input equal to 13\n"</span><span class="p">)</span><span class="w">
  </span><span class="p">}</span><span class="w">
  </span><span class="c1"># Do things</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">this</span><span class="p">()</span><span class="w">
</span>
## Error in this(): a, b and c can't be all NULL
<span class="n">this</span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">3</span><span class="p">)</span><span class="w">
</span>
## Warning in this(a = 3): using input below 5 is not recommended

## No input equal to 13
<span class="n">this</span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">10</span><span class="p">)</span><span class="w">
</span>
## No input equal to 13

To this:

<span class="n">this</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">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NULL</span><span class="p">){</span><span class="w">
  </span><span class="n">stop_if_all</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">),</span><span class="w"> </span><span class="n">is.null</span><span class="p">,</span><span class="w"> </span><span class="s2">"a, b and c can't be all NULL"</span><span class="p">)</span><span class="w">  
  </span><span class="n">warn_if_any</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">),</span><span class="w"> </span><span class="o">~</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">5</span><span class="p">,</span><span class="w"> </span><span class="s2">"using input below 5 is not recommended"</span><span class="p">)</span><span class="w">  
  </span><span class="n">message_if_none</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">),</span><span class="w"> </span><span class="o">~</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="p">,</span><span class="w"> </span><span class="s2">"No input equal to 13"</span><span class="p">)</span><span class="w">
  </span><span class="c1"># Do things</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>

The attempt() function, along with try_catch, are friendlier version
of try() and tryCatch(). They behave exactly like the base
functions, but provide an easier interface. For example, if you need to
try something and throw a message if it fails, with base, you’ll do:

<span class="n">x</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">try</span><span class="p">(</span><span class="nf">log</span><span class="p">(</span><span class="s2">"a"</span><span class="p">),</span><span class="w"> </span><span class="n">silent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">class</span><span class="p">(</span><span class="n">x</span><span class="p">)[</span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"try-error"</span><span class="p">){</span><span class="w">
  </span><span class="n">stop</span><span class="p">(</span><span class="s2">"There was an error in your code"</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>
## Error in eval(expr, envir, enclos): There was an error in your code

attempt() provides a concise way to send a message on error:

<span class="n">attempt</span><span class="p">(</span><span class="nf">log</span><span class="p">(</span><span class="s2">"a"</span><span class="p">),</span><span class="w"> </span><span class="s2">"There was an error in your code"</span><span class="p">)</span><span class="w">
</span>
## Error: There was an error in your code

Adverbs, finally, transform functions behavior:

  • silently returns nothing unless an error occurs

<span class="n">silent_log</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">silently</span><span class="p">(</span><span class="n">log</span><span class="p">)</span><span class="w">
</span><span class="n">silent_log</span><span class="p">(</span><span class="m">1</span><span class="p">)</span><span class="w">
</span><span class="n">silent_log</span><span class="p">(</span><span class="s2">"a"</span><span class="p">)</span><span class="w">
</span>
## Error in .f(...) : non-numeric argument to mathematical function
  • surely will wrap the function in an attempt call:

<span class="n">sure_log</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">surely</span><span class="p">(</span><span class="n">log</span><span class="p">)</span><span class="w">
</span><span class="n">sure_log</span><span class="p">(</span><span class="m">1</span><span class="p">)</span><span class="w">
</span>
## [1] 0
<span class="n">sure_log</span><span class="p">(</span><span class="s2">"a"</span><span class="p">)</span><span class="w">
</span>
## Error: non-numeric argument to mathematical function
  • The with_message() / with_warning() & without_message() /
    without_warning() / discretly() functions add or remove messages
    and
    warnings:

<span class="n">matrix</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="m">3</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">)</span><span class="w">
</span>
## Warning in matrix(1:3, ncol = 2): data length [3] is not a sub-multiple or
## multiple of the number of rows [2]

##      [,1] [,2]
## [1,]    1    3
## [2,]    2    1
<span class="n">no_warning_matrix</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">without_warning</span><span class="p">(</span><span class="n">matrix</span><span class="p">)</span><span class="w">
</span><span class="n">no_warning_matrix</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="m">3</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">)</span><span class="w">
</span>
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    1

Know more

See the GitHub repo for more.

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)