Note that there are some explanatory texts on larger screens.

plurals
  1. POCapture Arbitrary Conditions with `withCallingHandlers`
    text
    copied!<h1>The Problem</h1> <p>I'm trying to write a function that will evaluate code and store the results, including any possible conditions signaled in the code. I've got this working perfectly fine, except for the situation when my function (let's call it <code>evalcapt</code>) is run within an error handling expression.</p> <p>The problem is that <code>withCallingHandlers</code> will keep looking for matching condition handlers and if someone has defined such a handler outside of my function, my function loses control of execution. Here is simplified example of the problem:</p> <pre><code>evalcapt &lt;- function(expr) { conds &lt;- list() withCallingHandlers( val &lt;- eval(expr), condition=function(e) { message("Caught condition of class ", deparse(class(e))) conds &lt;&lt;- c(conds, list(e)) } ) list(val=val, conditions=conds) } myCondition &lt;- simpleCondition("this is a custom condition") class(myCondition) &lt;- c("custom", class(myCondition)) expr &lt;- expression(signalCondition(myCondition), 25) tryCatch(evalcapt(expr)) </code></pre> <p>Works as expected</p> <pre><code>Caught condition of class c("custom", "simpleCondition", "condition") $val [1] 25 $conditions $conditions[[1]] &lt;custom: this is a custom condition&gt; </code></pre> <p>but:</p> <pre><code>tryCatch( evalcapt(expr), custom=function(e) stop("Hijacked `evalcapt`!") ) </code></pre> <p>Doesn't work:</p> <pre><code>Caught condition of class c("custom", "simpleCondition", "condition") Error in value[[3L]](cond) : Hijacked `evalcapt`! </code></pre> <h1>A Solution I don't Know How To Implement</h1> <p>What I really need is a way of defining a restart right after the condition is signaled in the code which frankly is the way <code>withCallingHandlers</code> appears to work normally (when my handler is the last available handler), but I don't see the restart established when I <code>browse</code> in my handling function and use <code>computeRestarts</code>.</p> <h1>Things That Seem Like Solutions That Won't Work</h1> <h2>Use <code>tryCatch</code></h2> <p><code>tryCatch</code> does not have the same problem as <code>withCallingHandlers</code> because it does not continue looking for handlers after it finds the first one. The big problem with is it also does not continue to evaluate the code after the condition. If you look at the example that worked above, but sub in <code>tryCatch</code> for <code>withCallingHandlers</code>, the value (25) does not get returned because execution is brought back to the <code>tryCatch</code> frame after the condition is handled.</p> <p><strong>So basically, I'm looking for a hybrid between <code>tryCatch</code> and <code>withCallingHandlers</code>, one that returns control to the condition signaler, but also stops looking for more handlers after the first one is found.</strong></p> <h2>Break Up The Expression Into Sub-expression, then Use <code>tryCatch</code></h2> <p>Okay, but how do you break up (and more complex functions with signaled conditions all over the place):</p> <pre><code>fun &lt;- function(myCondition) { signalCondition(myCondition) 25 } expr &lt;- expression(fun()) </code></pre> <h1>Misc</h1> <p>I looked for the source code associated with the <code>.Internal(.signalCondition())</code> call to see if I can figure out if there is a behind the scenes restart being set, but I'm out of my depth there. It seems like:</p> <pre><code> void R_ReturnOrRestart(SEXP val, SEXP env, Rboolean restart) { int mask; RCNTXT *c; mask = CTXT_BROWSER | CTXT_FUNCTION; for (c = R_GlobalContext; c; c = c-&gt;nextcontext) { if (c-&gt;callflag &amp; mask &amp;&amp; c-&gt;cloenv == env) findcontext(mask, env, val); else if (restart &amp;&amp; IS_RESTART_BIT_SET(c-&gt;callflag)) findcontext(CTXT_RESTART, c-&gt;cloenv, R_RestartToken); else if (c-&gt;callflag == CTXT_TOPLEVEL) error(_("No function to return from, jumping to top level")); } } </code></pre> <p>from src/main/errors.c is doing some of that restart invocation, and this is called by <code>do_signalCondition</code>, but I don't have a clue how I would go about messing with this.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload