by Taylor Campbell (original text), John Cowan (revised text, shepherd), Wolfgang Corcoran-Mathe (revised implementation), Arvydas Silanskas (initial implementation)
This SRFI is currently in withdrawn status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to srfi-249@nospamsrfi.schemers.org
. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.
editor's summary of reasons for withdrawal: This SRFI was withdrawn by the editor because there had been no progress since 11-2023, and because he hadn't been able to reach the author since 5-2024. Wolfgang Corcoran-Mathe volunteered to take over with Marc Nieper-Wißkirchen.
Wolfgang wrote:
Marc and I are working out an improved (I think) approach to restarters which simplifies and corrects the semantics of SRFI 249’s restart mechanism. It’s a major departure from what’s described in SRFI 249, since it makes restarters a new condition type and uses the Scheme exception system rather than 249’s novel restarter stack.
As it stands, the system proposed by SRFI 249 is awkward to integrate with the Scheme exception system. It adds a new element to Scheme’s dynamic evaluation environment, a stack of restarters, but does not provide a convenient way to use this stack to recover from an exceptional situation: it leaves the dirty work to the user. Nor is stack management the full extent of the dirty work. SRFI 249 says nothing about the control behavior of a restarter, leaving it to the user of the library to ensure that a restarter’s invoker actually returns the interrupted computation to a sane situation. This is something that even the SRFI’s examples failed to take into a account.
A new SRFI was forked from this one as SRFI 255.
When an exceptional situation is encountered by a program, it may create a condition object describing the situation and then signal the condition and pass control to a condition handler. The signaler and handler are two different parts of a system, between which there is a barrier of abstraction. In order to recover gracefully and flexibly from exceptional situations, however, the signaler can provide multiple ways by which the handler can restart the computation, some of which may require extra input. Often, the decision of which method of recovery to choose is left up to a human user, who may be prompted for the input needed to recover. This SRFI proposes a simple mechanism called restarters to encapsulate the information necessary to restart a computation with associated interactive prompters.
An effective and flexible system for gracefully handling and recovering from exceptional situations is a necessity for any large software system. Most condition or exception systems provide only a one-way flow of information from the signaler to the handler, however: they offer no way to communicate possible methods of recovery to the signaler. Common Lisp, MIT Scheme, and Dylan are almost unique in this regard: they provide comprehensive facilities for restarting in exceptional situations. This proposal is considerably simpler than Common Lisp’s or Dylan’s restart systems, more in the spirit of Scheme, however. It is historically related to MIT Scheme’s system.
One important feature for a restart system to provide is interactivity. Though purely programmatic condition recovery is useful, it is well-acknowledged by the designers and users of Common Lisp’s condition system that the ability to interactively choose a method of recovery for a condition is useful. This ability, built-in to the Common Lisp language, is one of the primary reasons for the power of Common Lisp development and debugging environments. Though much of the baggage in Common Lisp’s restart system was deemed unnecessary for this proposal, interactivity is one crucial feature that was included. In this SRFI, the interactivity is provided by an interactor procedure that by default is provided by the implementation, but can be overridden by the user.
One major difference between the CL and Scheme condition systems is
that when a CL handler exits to its caller, the next outer handler is
invoked, whereas when a Scheme handler exits, either the code that
raised the condition is resumed (if raise-continuably
was
used), or another error is signaled (if raise
was used);
the condition must be re-raised in order to give the next outer handler
control. Therefore, in CL the only way for the signaler to regain
control is through a restart, but in Scheme restarts are just one way of
handling returns from exceptions.
A restarter is an object of a new disjoint type with three fields:
Restarters can be available in one of two ways. They can be ambient restarters, each of which is available during some dynamic extent of the program, or they can be the value of some variable or part of some data structure.
(make-restarter
tag description
invoker)
→ restarter
Returns a restarter with the specified tag, description, and invoker.
The tag argument is a symbol.
The description argument is a list whose car is a string that describes the effect of invoking the restarter. By convention it is a complete sentence in a natural language using the standard punctuation conventions of that language. It may also be a question or a command. The cdr of description is a list of strings that describe the values to be passed to the invoker in the same natural language: they may be phrases or whole sentences.
The invoker argument is a recovery procedure.
(restarter?
obj)
→
boolean
Returns #t
if obj is a restarter and
#f
otherwise.
Examples:
(restarter? return-zero-restarter) → #t
(restarter? 20) → #f
(restarter-tag
restarter)
→
tag
(restarter-description
restarter)
→
list-of-strings
Returns the tag / description of restarter. It is an error to mutate list-of-strings or any of its members.
(restart
restarter arg …)
→
values (may not return)
Invokes the invoker procedure of restarter on the
args, and returns however many values the invoker does. If the
invoker does not return, restart
does not return
either.
Invoking the return-zero
restarter:
(restart return-zero) → 0
(ambient-restarters)
Returns the current list of ambient restarters created by
make-restarter
and established by
with-restarter
. It is an error to mutate this list.
Ambient-restarters
is normally a SRFI 39 / R7RS parameter,
but directly modifying it with parameterize
should be
avoided.
(with-restarters
restarters
thunk)
Establishes restarters, which may be a single restarter, a
list of restarters, or a SRFI 222 compound object, as ambient restarters
on top of the existing ambient restarters. It is an error if any of the
restarters specified by restarters have the same tag. Then
with-restarter
invokes thunk with no arguments,
after which the restarters are disestablished and
with-restarter
returns whatever thunk returns.
(find-restarter
tag restarters)
→
restarter or #f
Searches restarters for a restarter whose tag is the same
(in the sense of symbol=?
) as tag. The
restarters argument may be a single restarter, a list of
restarters, or a SRFI 222
compound object. If no such restarter is found in restarters,
the value of (ambient-restarters)
is searched instead.
Failing that as well, #f
is returned.
(collect-restarters
restarters)
The argument restarters has the same semantics as the
restarters argument of find-restarter
. All
available restarters are collected into a list which is then returned,
giving the restarters in restarters priority over the
restarters in (ambient-restarters)
, but excluding those
whose tag is the same (in the sense of symbol=?
) as a
higher-priority restarter already on the list. Note that if
restarters is a list, earlier elements in the list take
priority over later ones, and the same is true for subobjects in a
compound object.
interactor
A SRFI 39 / R7RS parameter whose value is an interactor procedure. The general contract of such a procedure is as follows:
It accepts one argument, a list of restarters. The tags and the cars
of the descriptions of the restarters are made available to the user.
The user is then allowed to choose one of the tags.
Then the remaining strings in the description of the chosen restarter
are made available to the user, and the user is allowed to specify a
value corresponding to each string.
The interactor then calls restart
on the restarter and
the user’s values and returns whatever restart
returns.
The sample interactor outputs the tags and description strings with
display
and reads the chosen tag and values using
read
. Here is a possible example of the interaction:
The following actions are available:
return-zero: Return zero.
return-numerator: Return the numerator.
use-value: Choose a value to return.
abort: Abort the computation.
Which action do you choose: use-value
What is the value you wish to use? 32
In this case the restarter will return 32.
(restart-interactively
restarters)
→ values (may not return)
Equivalent to
((interactor) (collect-restarters
restarters))
The following tags by convention hold to particular behaviour protocols:
abort
Completely aborts the computation, usually returning to some sort of
initial user input such as a REPL. The invoker of an abort
restarter accepts zero arguments, is typically an ambient restarter, and
normally does not return.
ignore
Ignores the condition and proceeds. The invoker of an
ignore
restarter accepts zero arguments, is typically an
ambient restarter, and normally returns an unspecified value.
retry
Simply retries a whole computation from a certain point, with no
explicitly altered inputs. Some implicit environmental changes are
expected to have taken place. The invoker of a retry
restarter accepts zero arguments, may or may not be an ambient
restarter, and normally returns an unspecified value.
use-value
Retries a computation with a given input value substituted for some
invalid value in the original input. The invoker of a
use-value
restarter accepts at least one argument, the new
value(s) to substitute. It is may or may not be an ambient restarter,
and normally returns an unspecified value.
store-value
These restarter tag is in every respect like use-value
restarters except that it is meant to store the input value somewhere,
so that the old one is completely replaced, rather than just using the
input value temporarily and possibly accidentally reusing the old value
later and signaling another error.
It is highly recommended that Scheme systems integrate restarters into their condition systems and debuggers. This can be achieved by using SRFI 222 compound objects to represent conditions, with restarters as members to represent suitable recovery strategies among the subobjects.
The implementation is available at Github.
This SRFI is based on a proposal by Taylor Campbell.
© 2023 Taylor Campbell, John Cowan, Wolfgang Corcoran-Mathe), Arvydas Silanskas.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.