begin is used for grouping several expression together so that
they syntactically are treated as if they were one expression. This is
particularly important when syntactic expressions are used which only
allow one expression, but the programmer wants to use more than one
expression in that place. As an example, consider the conditional
expression below:
(if (> x 0)
(begin (display "greater") (newline)))
If the two calls to display and newline were not embedded
in a begin-statement, the call to newline would get
misinterpreted as the else-branch of the if-expression.
begin-expression. This expression type is used when the
expressions before the last one are evaluated for their side effects.
Guile provides three syntactic constructs for conditional evaluation.
if is the normal if-then-else expression (with an optional else
branch), cond is a conditional expression with multiple branches
and case branches if an expression has one of a set of constant
values.
#f, alternate is evaluated instead. The value of the
evaluated branch (consequent or alternate) is returned as
the value of the if expression.
When alternate is omitted and the test evaluates to
#f, the value of the expression is not specified.
cond-clause must look like this:
(test expression ...)
where test and expression are arbitrary expression, or like this
(test => expression
where expression must evaluate to a procedure.
The tests of the clauses are evaluated in order and as soon as one
of them evaluates to a true values, the corresponding expressions
are evaluated in order and the last value is returned as the value of
the cond-expression. For the => clause type,
expression is evaluated and the resulting procedure is applied to
the value of test. The result of this procedure application is
then the result of the cond-expression.
The test of the last clause may be the keyword else.
Then, if none of the preceding tests is true, the expressions following the else are evaluated to produce the result of the cond-expression.
((datum1 ...) expr1 expr2 ...)
and the last clause may have the form
(else expr1 expr2 ...)
All datums must be distinct. First, key is evaluated. The
the result of this evaluation is compared against all datums using
eqv?. When this comparison succeeds, the expression(s) following
the datum are evaluated from left to right, returning the value of
the last expression as the result of the case expression.
If the key matches no datum and there is an
else-clause, the expressions following the else are
evaluated. If there is no such clause, the result of the expression is
unspecified.
and and or evaluate all their arguments, similar to
begin, but evaluation stops as soon as one of the expressions
evaluates to false or true, respectively.
#f; the remaining expressions are
not evaluated. The value of the last evaluated expression is returned.
If no expression evaluates to #f, the value of the last
expression is returned.
If used without expressions, #t is returned.
#f); the remaining expressions are not evaluated. The value
of the last evaluated expression is returned. If all expressions
evaluate to #f, #f is returned.
If used without expressions, #f is returned.
Scheme has only few iteration mechanisms, mainly because iteration in
Scheme programs is normally expressed using recursion. Nevertheless,
R5RS defines a construct for programming loops, calling do. In
addition, Guile has an explicit looping syntax called while.
do expression. If
test evaluates to false, the commands are evaluated in
order, the steps are evaluated and stored into the variables
and the next iteration starts.
Any of the step expressions may be omitted, so that the corresponding variable is not changed during looping.
#f right from the start.
Another very common way of expressing iteration in Scheme programs is the use of the so-called named let.
Named let is a variant of let which creates a procedure and calls
it in one step. Because of the newly created procedure, named let is
more powerful than do--it can be used for iteration, but also
for arbitrary recursion.
let (see section Local Variable Bindings).
Named let works as follows:
let) to variable. The
new procedure's formal argument names are the name of the
variables.
The next example implements a loop which iterates (by recursion) 1000 times.
(let lp ((x 1000))
(if (positive? x)
(lp (- x 1))
x))
=>
0
The ability to explicitly capture continuations using
call-with-current-continuation (also often called call/cc
for short), and to invoke such continuations later any number of times,
and from any other point in a program, provides maybe the most powerful
control structure known. All other control structures, such as loops
and coroutines, can be emulated using continuations.
The implementation of continuations in Guile is not as efficient as one
might hope, because it is constrained by the fact that Guile is designed
to cooperate with programs written in other languages, such as C, which
do not know about continuations. So continuations should be used when
there is no other simple way of achieving the desired behaviour, or
where the advantages of the elegant continuation mechanism outweigh the
need for optimum performance. If you find yourself using call/cc
for escape procedures and your program is running too slow, you might
want to use exceptions (see section Exceptions) instead.
call-with-current-continuation, passing the arguments in a way
that they are returned by the call to
call-with-current-continuation. Since it is legal to store the
captured continuation in a variable or to pass it to other procedures,
it is possible that a procedure returns more than once, even if it is
called only one time. This can be confusing at times.
(define kont #f)
(call-with-current-continuation
(lambda (k)
(set! kont k)
1))
=>
1
(kont 2)
=>
2
Scheme allows a procedure to return more than one value to its caller. This is quite different to other languages which only allow single-value returns. Returning multiple values is different from returning a list (or pair or vector) of values to the caller, because conceptually not one compound object is returned, but several distinct values.
The primitive procedures for handling multiple values are values
and call-with-values. values is used for returning
multiple values from a procedure. This is done by placing a call to
values with zero or more arguments in tail position in a
procedure body. call-with-values combines a procedure returning
multiple values with a procedure which accepts these values as
parameters.
call-with-values procedure,
all continuations take exactly one value. The effect of
passing no value or more than one value to continuations that
were not created by call-with-values is unspecified.
call-with-values.
(call-with-values (lambda () (values 4 5))
(lambda (a b) b))
==> 5
(call-with-values * -) ==> -1
In addition to the fundamental procedures described above, Guile has a
module which exports a syntax called receive, which is much more
convenient. If you want to use it in your programs, you have to load
the module (ice-9 receive) with the statement
(use-modules (ice-9 receive))
lambda (see section Lambda: Basic Procedure Creation). After binding the variables,
the expressions in body ... are evaluated in order.
A common requirement in applications is to want to jump non-locally from the depths of a computation back to, say, the application's main processing loop. Usually, the place that is the target of the jump is somewhere in the calling stack of procedures that called the procedure that wants to jump back. For example, typical logic for a key press driven application might look something like this:
main-loop: read the next key press and call dispatch-key dispatch-key: lookup the key in a keymap and call an appropriate procedure, say find-file find-file: interactively read the required file name, then call find-specified-file find-specified-file: check whether file exists; if not, jump back to main-loop ...
The jump back to main-loop could be achieved by returning through
the stack one procedure at a time, using the return value of each
procedure to indicate the error condition, but Guile (like most modern
programming languages) provides an additional mechanism called
exception handling that can be used to implement such jumps much
more conveniently.
There are several variations on the terminology for dealing with non-local jumps. It is useful to be aware of them, and to realize that they all refer to the same basic mechanism.
Where signal and signalling are used, special care is needed to avoid the risk of confusion with POSIX signals. (Especially considering that Guile handles POSIX signals by throwing a corresponding kind of exception: REFFIXME.)
This manual prefers to speak of throwing and catching exceptions, since this terminology matches the corresponding Guile primitives.
catch is used to set up a target for a possible non-local jump.
The arguments of a catch expression are a key, which
restricts the set of exceptions to which this catch applies, a
thunk that specifies the normal case code -- i.e. what should
happen if no exceptions are thrown -- and a handler procedure
that says what to do if an exception is thrown. Note that if the
normal case thunk executes normally, which means without
throwing any exceptions, the handler procedure is not executed at all.
When an exception is thrown using the throw primitive, the first
argument of the throw is a symbol that indicates the type of the
exception. For example, Guile throws an exception using the symbol
numerical-overflow to indicate numerical overflow errors such as
division by zero:
(/ 1 0) => ABORT: (numerical-overflow)
The key argument in a catch expression corresponds to this
symbol. key may be a specific symbol, such as
numerical-overflow, in which case the catch applies
specifically to exceptions of that type; or it may be #t, which
means that the catch applies to all exceptions, irrespective of
their type.
The second argument of a catch expression should be a thunk
(i.e. a procedure that accepts no arguments) that specifies the normal
case code. The catch is active for the execution of this thunk,
including any code called directly or indirectly by the thunk's body.
Evaluation of the catch expression activates the catch and then
calls this thunk.
The third argument of a catch expression is a handler procedure.
If an exception is thrown, this procedure is called with exactly the
arguments specified by the throw. Therefore, the handler
procedure must be designed to accept a number of arguments that
corresponds to the number of arguments in all throw expressions
that can be caught by this catch.
(handler key args ...)
key is a symbol or #t.
thunk takes no arguments. If thunk returns
normally, that is the return value of catch.
Handler is invoked outside the scope of its own catch.
If handler again throws to the same key, a new handler
from further up the call chain is invoked.
If the key is #t, then a throw to any symbol will
match this call to catch.
If the handler procedure needs to match a variety of throw
expressions with varying numbers of arguments, you should write it like
this:
(lambda (key . args) ...)
The key argument is guaranteed always to be present, because a
throw without a key is not valid. The number and
interpretation of the args varies from one type of exception to
another, but should be specified by the documentation for each exception
type.
Note that, once the handler procedure is invoked, the catch that led to the handler procedure being called is no longer active. Therefore, if the handler procedure itself throws an exception, that exception can only be caught by another active catch higher up the call stack, if there is one.
The throw primitive is used to throw an exception. One argument,
the key, is mandatory, and must be a symbol; it indicates the type
of exception that is being thrown. Following the key,
throw accepts any number of additional arguments, whose meaning
depends on the exception type. The documentation for each possible type
of exception should specify the additional arguments that are expected
for that kind of exception.
key is a symbol. It will match catches of the same symbol or of
#t.
If there is no handler at all, Guile prints an error and then exits.
When an exception is thrown, it will be caught by the innermost
catch expression that applies to the type of the thrown
exception; in other words, the innermost catch whose key is
#t or is the same symbol as that used in the throw
expression. Once Guile has identified the appropriate catch, it
handles the exception by applying that catch expression's handler
procedure to the arguments of the throw.
If there is no appropriate catch for a thrown exception, Guile
prints an error to the current error port indicating an uncaught
exception, and then exits. In practice, it is quite difficult to
observe this behaviour, because Guile when used interactively installs a
top level catch handler that will catch all exceptions and print
an appropriate error message without exiting. For example, this
is what happens if you try to throw an unhandled exception in the
standard Guile REPL; note that Guile's command loop continues after the
error message:
guile> (throw 'badex) <unnamed port>:3:1: In procedure gsubr-apply ... <unnamed port>:3:1: unhandled-exception: badex ABORT: (misc-error) guile>
The default uncaught exception behaviour can be observed by evaluating a
throw expression from the shell command line:
$ guile -c "(begin (throw 'badex) (display \"here\\n\"))" guile: uncaught throw to badex: () $
That Guile exits immediately following the uncaught exception
is shown by the absence of any output from the display
expression, because Guile never gets to the point of evaluating that
expression.
A lazy catch is used in the same way as a normal catch,
with key, thunk and handler arguments specifying the
exception type, normal case code and handler procedure, but differs in
one important respect: the handler procedure is executed without
unwinding the call stack from the context of the throw expression
that caused the handler to be invoked.
catch, except that it does
not unwind the stack before invoking handler.
The handler procedure is not allowed to return:
it must throw to another catch, or otherwise exit non-locally.
Typically, handler should save any desired state associated with
the stack at the point where the corresponding throw occurred,
and then throw an exception itself -- usually the same exception as the
one it caught. If handler is invoked and does not throw an
exception, Guile itself throws an exception with key misc-error.
Not unwinding the stack means that throwing an exception that is caught
by a lazy-catch is almost equivalent to calling the
lazy-catch's handler inline instead of each throw, and
then omitting the surrounding lazy-catch. In other words,
(lazy-catch 'key (lambda () ... (throw 'key args ...) ...) handler)
is almost equivalent to
((lambda () ... (handler 'key args ...) ...))
But why only almost? The difference is that with
lazy-catch (as with normal catch), the dynamic context is
unwound back to just outside the lazy-catch expression before
invoking the handler. (For an introduction to what is meant by dynamic
context, See section Dynamic Wind.)
Then, when the handler itself throws an exception, that exception
must be caught by some kind of catch (including perhaps another
lazy-catch) higher up the call stack.
The dynamic context also includes with-fluids blocks (REFFIXME),
so the effect of unwinding the dynamic context can also be seen in fluid
variable values. This is illustrated by the following code, in which
the normal case thunk uses with-fluids to temporarily change the
value of a fluid:
(define f (make-fluid))
(fluid-set! f "top level value")
(define (handler . args)
(cons (fluid-ref f) args))
(lazy-catch 'foo
(lambda ()
(with-fluids ((f "local value"))
(throw 'foo)))
handler)
=>
("top level value" foo)
((lambda ()
(with-fluids ((f "local value"))
(handler 'foo))))
=>
("local value" foo)
In the lazy-catch version, the unwinding of dynamic context
restores f to its value outside the with-fluids block
before the handler is invoked, so the handler's (fluid-ref f)
returns the external value.
lazy-catch is useful because it permits the implementation of
debuggers and other reflective programming tools that need to access the
state of the call stack at the exact point where an exception or an
error is thrown. For an example of this, see REFFIXME:stack-catch.
It is traditional in Scheme to implement exception systems using
call-with-current-continuation. Continuations
(see section Continuations) are such a powerful concept that any other
control mechanism -- including catch and throw -- can be
implemented in terms of them.
Guile does not implement catch and throw like this,
though. Why not? Because Guile is specifically designed to be easy to
integrate with applications written in C. In a mixed Scheme/C
environment, the concept of continuation must logically include
"what happens next" in the C parts of the application as well as the
Scheme parts, and it turns out that the only reasonable way of
implementing continuations like this is to save and restore the complete
C stack.
So Guile's implementation of call-with-current-continuation is a
stack copying one. This allows it to interact well with ordinary C
code, but means that creating and calling a continuation is slowed down
by the time that it takes to copy the C stack.
The more targeted mechanism provided by catch and throw
does not need to save and restore the C stack because the throw
always jumps to a location higher up the stack of the code that executes
the throw. Therefore Guile implements the catch and
throw primitives independently of
call-with-current-continuation, in a way that takes advantage of
this upwards only nature of exceptions.
Guile provides a set of convenience procedures for signaling error conditions that are implemented on top of the exception primitives just described.
misc-error and a message constructed by
displaying msg and writing args.
#f.
message is the error message string, possibly containing
~S and ~A escapes. When an error is reported,
these are replaced by formatting the corresponding members of
args: ~A (was %s in older versions of
Guile) formats using display and ~S (was
%S) formats using write. data is a list or
#f depending on key: if key is
system-error then it should be a list containing the
Unix errno value; If key is signal then it
should be a list containing the Unix signal number; otherwise
it will usually be #f.
#f is returned instead.
[FIXME: this is pasted in from Tom Lord's original guile.texi and should be reviewed]
If, any time during the execution of thunk, the
continuation of the dynamic_wind expression is escaped
non-locally, out_guard is called. If the continuation of
the dynamic-wind is re-entered, in_guard is called. Thus
in_guard and out_guard may be called any number of
times.
(define x 'normal-binding) => x (define a-cont (call-with-current-continuation (lambda (escape) (let ((old-x x)) (dynamic-wind ;; in-guard: ;; (lambda () (set! x 'special-binding)) ;; thunk ;; (lambda () (display x) (newline) (call-with-current-continuation escape) (display x) (newline) x) ;; out-guard: ;; (lambda () (set! x old-x))))))) ;; Prints: special-binding ;; Evaluates to: => a-cont x => normal-binding (a-cont #f) ;; Prints: special-binding ;; Evaluates to: => a-cont ;; the value of the (define a-cont...) x => normal-binding a-cont => special-binding
Error handling is based on catch and throw. Errors are
always thrown with a key and four arguments:
#f.
~A and ~S can be
embedded within the message: they will be replaced with members of the
args list when the message is printed. ~A indicates an
argument printed using display, while ~S indicates an
argument printed using write. message can also be
#f, to allow it to be derived from the key by the error
handler (may be useful if the key is to be thrown from both C and
Scheme).
~A and
~S tokens in message. Can also be #f if no
arguments are required.
'system-error, this contains the C errno value. Can also
be #f if no additional objects are required.
In addition to catch and throw, the following Scheme
facilities are available:
'misc-error. The error
message is created by displaying msg and writing the args.
The following are the error keys defined by libguile and the situations in which they are used:
error-signal: thrown after receiving an unhandled fatal signal
such as SIGSEGV, SIGBUS, SIGFPE etc. The rest argument in the throw
contains the coded signal number (at present this is not the same as the
usual Unix signal number).
system-error: thrown after the operating system indicates an
error condition. The rest argument in the throw contains the
errno value.
numerical-overflow: numerical overflow.
out-of-range: the arguments to a procedure do not fall within the
accepted domain.
wrong-type-arg: an argument to a procedure has the wrong type.
wrong-number-of-args: a procedure was called with the wrong number
of arguments.
memory-allocation-error: memory allocation error.
stack-overflow: stack overflow error.
regex-error: errors generated by the regular expression library.
misc-error: other errors.
SCM scm_error (SCM key, char *subr, char *message, SCM args, SCM rest)
Throws an error, after converting the char * arguments to Scheme strings. subr is the Scheme name of the procedure, NULL is converted to #f. Likewise a NULL message is converted to #f.
The following procedures invoke scm_error with various error keys and arguments. The first three call scm_error with the system-error key and automatically supply errno in the "rest" argument: scm_syserror generates messages using strerror, scm_sysmissing is used when facilities are not available. Care should be taken that the errno value is not reset (e.g. due to an interrupt).
Exception handlers can also be installed from C, using scm_internal_catch, scm_lazy_catch, or scm_stack_catch from libguile/throw.c. These have not yet been documented, however the source contains some useful comments.
Go to the first, previous, next, last section, table of contents.