As an expression, the begin
syntax is used to evaluate a sequence
of sub-expressions in order. Consider the conditional expression below:
(if (> x 0) (begin (display "greater") (newline)))
If the test is true, we want to display “greater” to the current
output port, then display a newline. We use begin
to form a
compound expression out of this sequence of sub-expressions.
The expression(s) are evaluated in left-to-right order and the values of
the last expression are returned as the result of the
begin
-expression. This expression type is used when the
expressions before the last one are evaluated for their side effects.
The begin
syntax has another role in definition context
(see Internal definitions). A begin
form in a definition
context splices its subforms into its place. For example,
consider the following procedure:
(define (make-seal) (define-sealant seal open) (values seal open))
Let us assume the existence of a define-sealant
macro that
expands out to some definitions wrapped in a begin
, like so:
(define (make-seal) (begin (define seal-tag (list 'seal)) (define (seal x) (cons seal-tag x)) (define (sealed? x) (and (pair? x) (eq? (car x) seal-tag))) (define (open x) (if (sealed? x) (cdr x) (error "Expected a sealed value:" x)))) (values seal open))
Here, because the begin
is in definition context, its subforms
are spliced into the place of the begin
. This allows the
definitions created by the macro to be visible to the following
expression, the values
form.
It is a fine point, but splicing and sequencing are different. It can make sense to splice zero forms, because it can make sense to have zero internal definitions before the expressions in a procedure or lexical binding form. However it does not make sense to have a sequence of zero expressions, because in that case it would not be clear what the value of the sequence would be, because in a sequence of zero expressions, there can be no last value. Sequencing zero expressions is an error.
It would be more elegant in some ways to eliminate splicing from the
Scheme language, and without macros (see Macros), that would be a
good idea. But it is useful to be able to write macros that expand out
to multiple definitions, as in define-sealant
above, so Scheme
abuses the begin
form for these two tasks.