Lack of ambient restarters Daphne Preston-Kendal (05 Nov 2024 23:53 UTC)
Re: Lack of ambient restarters Wolfgang Corcoran-Mathe (06 Nov 2024 02:34 UTC)

Re: Lack of ambient restarters Wolfgang Corcoran-Mathe 06 Nov 2024 02:34 UTC

Daphne,

On 2024-11-06 00:53 +0100, Daphne Preston-Kendal wrote:
> The SRFI claims that since the R6RS/R7RS small exception system
> already deals with establishing a stack of exception handlers, a
> separate stack of restarters is no longer needed.
>
> I think this is incorrect. The R6RS/R7RS small system doesn’t give
> you any way to immediately get the full stack of exception handlers
> in effect at the point raised; you can only dynamically invoke them
> one after the other. So there is no way to install a restarter far up
> the stack and have that immediately appear as a recovery option at the
> immediate moment of an error. In particular, the ‘ambient restarter’
> functionality of the original SRFI and pre-SRFI is gone.

In fact, it isn’t, at least if you install restarters with the provided
syntax (‘restarter-guard’, ‘restartable’, etc.); all of the restarters
installed between the error-point and the interactor in the handler
stack will be available. The key is that ‘restarter-guard’ raises a
composite object comprising (a) the raised condition and (b) all of
that guard’s restarters. So a series of nested restarter-guard forms
will roll up all of their restarters and pass them up the stack.

There is a small example of this in the SRFI. Here’s part of it:

    > (with-exception-handler
        default-interactor
        (lambda ()
          (map-restartable "divider"
                           (lambda (x) (/ 10 x)))
                           '(1 2 0 4))))

    Restartable exception occurred.
    (use-arguments . args) [divider]: Apply the procedure to new
        arguments.
    (use-list new-lis) [map-restartable]: Return new-lis as the
        value of map-restartable.
    restart[0]> (use-list '(#f))

      ⇒ (#f)

If we’d really lost the “ambient restarter functionality”, it wouldn’t
be possible to choose to restart the map here, rather than the mapped
procedure. But it is. (We should probably make this clearer in the
SRFI.)

All bets are off if library users construct and install restarters by
hand. This may be another argument for hiding ‘make-restarter’.

You mentioned on IRC that SRFI 255 also lacks a way to get a list of all
currently-installed restarters (the equivalent of ‘collect-restarters’
from SRFI 249). I don’t think we can implement ‘collect-restarters’
within the SRFI 255 model, since it would have to unwind the handler
stack “all the way to the top” before returning a list of restarters
to its original continuation. The best I can come up with at the
moment is a delimited form like this:

    (call-with-restarter-collector
     (lambda (get-restarters)
       (restarter-guard frob (con1 ((frobnicate x) "" x))
         (restarter-guard bar (con2 ((barnaclize y) "" y))
           (map restarter-tag (get-restarters))))))

    ⇒ (frobnicate barnaclize)

Using (call-with-restarter-collector *proc*) makes all restarters
installed within *proc* available by calling the passed collector
procedure. This is easy to implement, if not especially beautiful.

> I assume similar considerations are why the current-interactor
> parameter of SRFI 249 was also removed. I think this should be
> restored as well.

Can you suggest how we could restore it? Interactors in SRFI 255 are
just talkative exception handlers, so you install them using
‘with-exception-handler’ or ‘guard’.

(A limitation of SRFI 255: it is not possible—or at least it’s quite
difficult—to dynamically change the current interactor, i.e. from “deep
in the stack” and while preserving the currently-installed restarters.
With SRFI 249’s ‘interactor’ parameter, you can do this. Considering
that this allows code to hijack your interactor, I’m not entirely sure
it’s a good feature.)

--
Wolfgang Corcoran-Mathe  <xxxxxx@sigwinch.xyz>