[Rd] "try"ing to understand condition handling

Luke Tierney luke at stat.uiowa.edu
Wed Feb 21 00:28:24 CET 2007


On Tue, 20 Feb 2007, Ross Boylan wrote:

> Thanks; your response is very helpful.  This message has some remarks
> on my questions relative to the developer docs, one additional
> question, and some documentation comments.
>
> I'm really glad to hear you plan to revise the exception/condition
> docs. since I  found the existing ones a bit murky.
>
> Below, [1] means
> http://www.stat.uiowa.edu/~luke/R/exceptions/simpcond.html,
> one of the documents Prof Ripley referred to.
>
> That page also has a nice illustration of using the restart facility.
>
> On Tue, Feb 20, 2007 at 01:40:11PM -0600, Luke Tierney wrote:
>> On Mon, 19 Feb 2007, Ross Boylan wrote:
>>
>>> I'm confused by the page documenting tryCatch and friends.
>>>
>>> I think it describes 3 separate mechanisms: tryCatch (in which control
>>> returns to the invoking tryCatch), withCallHandlers (in which
>  should have been "withCallingHandlers"
>>> control
>>> goes up to the calling handler/s but then continues from the point at
>>> which signalCondition() was invoked),
>>
>> unless a handler does a non-local exit, typically by invoking a restart
>>
>>> and withRestarts (I can't tell
>>> where control ends up).
>>
>> at the withRestarts call
>>
>>> For tryCatch the docs say the arguments ... provide handlers, and that
>>> these are matched to the condition.  It appears that matching works by
>>> providing entries in ... as named arguments, and the handler matches
>>> if the name is one of the classes of the condition.  Is that right?  I
>>> don't see the matching rule explicitly stated.  And then  the handler
>>> itself is a single argument function, where the argument is the
>>> condition?
> From [1], while discussing tryCatch,
>
> Handlers are specified as
>
> name = fun
>
> where name specifies an exception class and fun is a function of one
> argument, the condition that is to be handled.
>
>
> ...
>>>
>>> Also, the documents don't explicitly say that the abstract subclasses
>>> of 'error' and 'warning' are subclasses of 'condition', though that
>>> seems to be implied and true.
> The class relations are explicit in [1].
>
>>>
>>> It appears that for tryCatch only the first matching handler is
>>> executed, while for withCallHandlers all matching handlers are
>>> executed.
>>
>> All handlers are executed, most recently established first, until
>> there are none left or there is a transfer of control.  Conceptually,
>> exiting handlers established with tryCatch execute a transfer of
>> control and then run their code.
>
> Here's the one point of clarification: does the preceding paragraph
> about "all handlers are executed" apply only to withCallingHandlers,
> or does it include tryCatch as well?  Rereading ?tryCatch, it still
> looks as if the first match only will fire.
> ....

Only to calling handlers because exiting ones conceptually execute a
transfer of control out of the loop over the eligible handlers.

[In principle tryCatch can be written in terms of withCallingHandlers
using callCC to transfer control (callCC will be in R-devel shortly
and corresponds to callcc in [1]).  The expression

     tryCatch(expr, c1 = h1, c2 = h2)

is essentially equivalent to something like (untested but hopefully close)

     cond <- NULL
     h <- NULL
     v <- callCC(function(k)
         withCallingHandlers(expr,
                             c1 = function(c) { cond <<- c; h <<- h1; k(NULL)},
                             c2 = function(c) { cond <<- c; h <<- h2; k(NULL)}))
     if (is.null(h)) value
     else h(cond)

Matching calling handlers are applied one at a time, but if one of
these two matches then the call k(NULL) causes an immediate return
from callCC with value NULL.  If this doesn't help, ignore it.]

>>
>> Hopefully a more extensive document on this will get written in the
>> next few months; for now the notes available off the developer page
>> may be useful.
>>
>> best,
>>
>> luke
> Great.  FWIW, here are some suggestions about the documentation:
>
> I would find a presentation that provided an overall orientation and
> then worked down easiest to follow.  So, goiing from the top down:
> 1. there are 3 forms of exception handling: try/catch, calling
> handlers and restarts.
> 2. the characteristic behavior of each is ... (i.e., what's the flow
> of control).  Maybe give a snippet of typical uses of each.
> 3. the details (exact calling environment of the handler(s), matching
> rules, syntax...)
> 4. try() is basically a convenient form of tryCatch.
> 5. Other relations between these 3 forms: what happens if they are
> nested; how restarts alter the "standard" control flow of the other
> forms.  I also found the info that the restart mechanism is the most
> general and complicated useful for orientation (that might go under
> point 1).
>
> It might be appropriate to document each form on a separate manual
> page; I'm not sure if they are too linked (particularly by the use of
> conditions and the control flow of restart) to make that a good idea.
>
> I notice that some of the outline above is not the standard R manual
> format; maybe the big picture should go in the language manual or on a
> concept page (?Exceptions maybe).
>
> Be explicit about the relations between conditions (class inheritance
> relations).
>
> Be explicit about how handlers are chosen and which forms they take.
>
> It might be worth mentioning stuff that is a little surprising.  The
> fact that the value of  the finally is not the value of the tryCatch
> was a little surprising, since usually the value of a series of
> statements or expression is that of the last one.  The fact that
> signalCondition can participate in two different flows of control
> (discussion snipped above) was also surprising to me.  In both cases
> the current ?tryCatch is pretty explicit already, so that's not the
> issue.
>
> I found the current language (for ?tryCatch) about the calling context
> of different handlers a bit obscure.  For example, discussing tryCatch:
>   " If a handler
>     is found then control is transferred to the 'tryCatch' call that
>     established the handler, the handler found and all more recent
>     handlers are disestablished, the handler is called with the
>     condition as its argument, and the result returned by the handler
>     is returned as the value of the 'tryCatch' call."
> It seems to me that control is transferred to within the tryCatch
> call, rather than to the call itself.  I'd expect transferring control
> to the call to re-execute the whole thing, ad infinitum.  I found the
> seemingly less formal description in [1] easier to grasp:
> " When an exception is signaled, the most recently established handler
> that matches the exception (for which the exception inherits from the
> specified class) is chosen, control transfers back to the try.catch
> expression, the handler function is called, and the value returned by
> the handler function is returned by the try.catch call. "
>
> ?tryCatch refers to "up" and "down" the stack in a few places.
> Although there is really only one plausible reading, referring to
> handlers established before or after the focal handler might be
> clearer (at least in my head, I have an image of call stacks sometimes
> going down in physical memory as they grow).
>

Thanks for the suggestions.  Will keep them in mind for the revision.

Best,

luke


> Ross
>

-- 
Luke Tierney
Chair, Statistics and Actuarial Science
Ralph E. Wareham Professor of Mathematical Sciences
University of Iowa                  Phone:             319-335-3386
Department of Statistics and        Fax:               319-335-3017
    Actuarial Science
241 Schaeffer Hall                  email:      luke at stat.uiowa.edu
Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu



More information about the R-devel mailing list