2. Procedures
A procedure is an abstraction of an operation over
objects. The operation thus abstracted over may be performed by a call
to the procedure. When a procedure is called, it receives a number of
inputs, referred to as its arguments. The procedure call processes those
arguments either according to a specification defined in this report or
by an implementation, or by evaluating some Scheme code which operates
on those arguments, to perform the operation and produce its outputs.
The procedure then returns its outputs to the continuation of the
expression which called it. A procedure whose operation is defined by
Scheme code is created by the lambda or case-lambda expressions: the arguments to the procedure are made available to that
code as variables bound within the region of that code, which is a
body (see Section 2.6, “Bodies”). Additional inputs to the procedure may come from variables whose
region encompasses the corresponding lambda or case-lambda expression.
Scheme procedures are also objects in their own right.
Procedures can be created dynamically, stored in data structures, returned
as results of procedures, and so on. Many predefined operations of Scheme
are provided not by syntax, but by variables whose values are procedures.
The + operation, for example, which receives special
syntactic treatment in many other languages, is just a regular identifier
in Scheme, bound to a procedure that adds number objects.
All procedure objects in Scheme are conceptually
associated with location tags, in order to make the eq? and eqv? procedures (section ???) work on procedures. The nature of this location tag is unspecified:
the location tag itself cannot be directly treated as an object in
Scheme; it is required only to have a defined equivalence relation for
the purposes of the internal workings of eq? and eqv?.
2.1. Rationale
Besides their obvious uses, procedures have
traditionally been used in some Scheme programs as a form of data
abstraction for object-oriented programming and similar techniques
(see for example AbelsonAndSussman96 section 2.1.3). This makes it convenient to have a form of equivalence predicate
defined over them, as for other Scheme data objects. However, treating lambda as a data constructor subject to the usual
requirement to return a freshly-allocated object upon each evaluation
(in the same way as, for example, the cons procedure)
disallows some useful compiler optimizations for the more common case
of using lambda to abstract a series of operations,
rather than a collection of data.
A future fascicle will define a data constructor for
procedures which guarantees that every evaluation creates a procedure
object which is distinct from every other object in the sense of eqv? and eq?. A future Scheme report
may then make the equivalence of all other procedures under eq? and eqv? unspecified. (The R6RS already made this change, but neglected to provide a suitable alternative
for creating procedures which are actually used as data, so the change
was reverted by the small language report.)
2.2. Description
Every named procedure defined by this report has a single location tag distinct from the location tag of procedures with distinct names. The exception is if this report describes one procedure as an alias of another, in which case the two procedures may or may not have distinct location tags.
2.3. Expressions and definitions
All main forms in Scheme program and library code can be categorized as either definitions or expressions.
Definitions may appear directly within a program, a
library , or
a body (see section Section 2.6, “Bodies”). They may also appear within a begin form (see
section ???) or other splicing form that appears within one
of the contexts where a definition may appear. A definition has the
effect of binding one or more identifiers to variables or syntax
keywords within the region of that program, library, or body in which
it is found, and within any environments created by extending the
environment in place in that region. A definition which appears within
a library or splicing form within a library has the effect of allowing
the identifiers it names to be exported from that library . A definition conceptually does not evaluate to any value or values,
although a definition form which is passed to the eval procedure may cause that procedure call to return a value .
Expressions evaluate to a value or values (see section Section 2.5, “Multiple return values”). They may contain a region for an identifier or identifiers created by the expression itself, or by definitions which form part of the expression, but expressions do not create bindings in the region surrounding themselves. Expressions may occur within other expressions, as well as within definitions to specify the values of variables or the transformers for syntax keywords that are bound by the definition. All syntactic forms defined in this report are expressions unless explicitly stated to be definitions.
When an expression appears within a definition, or within a syntactic form or procedure call that changes the value of a variable or the value stored in some other location, the expression which denotes the new value to be stored in that location is said to be on the right-hand side of the definition, assignment form, or procedure call. The remaining subforms or arguments, which denote the location which is to have a new value stored in it, are said to be on the left-hand side.
When syntactic forms are specified in this report, the
rule expression means any form which is an expression,
and definition means any form which is a definition.
Definition or expression means either a definition
or an expression, although the final sequence of forms within any
context where either is allowed must conform to the restrictions
on definitions and expressions which apply within that context.
For example, one such restriction which applies to all contexts
where definitions are allowed is that no definition form may attempt
to bind an identifier which another definition form in the same
context also binds. It is a syntax violation to use a definition
in a context where only expressions are allowed.
Some main forms in Scheme also contain auxiliary forms
which are
neither definitions nor expressions. Such auxiliary forms, unlike main
forms, have no meaning or validity outside of by the main form to
which they belong. Examples of such auxiliary forms include the formals clause of a lambda expression (see
section Section 2, “Procedures”), the left-hand side of define forms and the bindings clauses of let expressions (see
section ???), and each cond clause of a cond expression (see section ???).
2.4. Continuations and dynamic extents
NOTE: This section will be extended by a future fascicle.
Whenever a Scheme expression is evaluated there is a
continuation waiting for the result of the expression. The continuation
represents an entire (default) future for the computation. For
example, informally the continuation of 3 in the expression (+ 1 3) adds 1 to it.
Normally these ubiquitous continuations are hidden behind the scenes and programmers do not think much about them. However, one distinguishing feature of Scheme is that continuations, which in most other languages only operate behind the scenes, also have first-class status. Continuations may be captured in first-class procedure objects and returned to later by subsequent calls to these procedures. Using this mechanism, continuations in Scheme may never be returned to, or they may be returned to multiple times. Thus, the evaluation of any expression or sequence of expressions in Scheme can be interrupted and restarted multiple times by capture and return to a continuation.
For a procedure call, the time between when it is initiated and when it returns is called its dynamic extent. Capture of continuations allows re-entering a dynamic extent after its procedure call has returned. Thus, the dynamic extent of a call may not be a single, connected time period.
2.5. Multiple return values
A Scheme expression can evaluate to an arbitrary finite number of values. These values are passed to the expression’s continuation.
Not all continuations accept any number of values. For example, a continuation that accepts the argument to a procedure call is guaranteed to accept exactly one value (see section Chapter 3, Basic expression types).
A number of forms in Scheme have sequences of expressions as subforms that are evaluated sequentially, with the return values of all but the last expression being discarded. The continuations discarding these values accept any number of values.
2.6. Bodies
Many syntactic forms in Scheme are specified as
containing a body. (In order to distinguish bodies from the code
inside of libraries and top-level programs, a body is sometimes
referred to as a procedure body, because lambda is
the fundamental form for the implementation of forms which use
bodies.) A body is a region for keywords and variables created by
definitions directly within the body, or inside of splicing constructs
(such as begin, splicing-let-syntax, and splicing-letrec-syntax) within the body. A
body is concluded with an expression and may also contain additional
expressions before, between, or after the definitions.
Bodies are first processed by the expander according to the process defined in , a process which also removes any splicing constructs by replacing them with their expanded subforms. The expander also takes note of the set of identifier names bound by syntax or variable definitions within the entire region of the body. It is a syntax violation if any definition attempts to bind the same identifier more than once, or attempts to bind the same identifier as another definition within the same body (including in any splicing constructs).
After this process, a body consists only of variable
definitions and
expressions in their fully expanded core forms. These are further
processed for evaluation in groups as follows. An expanded body has the form
variable definition–expression group...expression
The expression is referred to as the final
expression of the body. A variable definition–expression group has the form
variable definition...expression
A variable definition is any
implementation-dependent core form which is a definition form for
variables. The expressions of a variable definition–expression group are referred to as
the non-final expressions of the body. It is a syntax violation if the
expanded content of a body does not conform to these rules.
Each variable definition–expression group is converted into the equivalent of a letrec*-values (see section ???) for the variable definitions in that group, although
this letrec*-values does not contain a body itself but
only a sequence of expressions, which are evaluated from left to right.
The sequence of expressions consists of the expressions
within the respective variable definition–expression group, followed by the
corresponding conversion of the next variable definition–expression group, or by the final expression if there are no further variable definition–expression groups. Implementations
should signal a syntax violation if the expansion of the right-hand side
of any variable definition, or the expansion of any
expression, refers to the name of any identifier which is
a variable defined by a variable definition–expression group which is to the right of the variable definition–expression group in which it appears.
RationaleScheme has traditionally only allowed a single group of
definitions at the head of a body, followed by at least one
expression. Experience has shown that it is often useful to be able
to mix the two types of forms in bodies, especially for error
checking and logging of intermediate results. However, typical
optimized implementation techniques for the semantics of the letrec or letrec* underlying a
traditional Scheme body work only when the right-hand sides of
definitions, and by extension the entire block of definitions, are
free of side-effects or uses of first-class continuations. Since
non-final expressions in a body are only useful for their side-effects
or continuation invocations, simply incorporating expressions into
the sequence of letrec* bindings invariably inhibits
these optimizations. The compilation scheme described here provides
the convenience of mixing definitions and expressions in any order,
while ultimately grouping the definitions so that optimization can
still happen as in implementations of previous revisions of this
report. Since the expander still treats syntax keyword bindings
throughout the whole body as belonging to a single region, and
implementations should treat attempts at forward reference to a
variable defined in a future group of definitions and expressions
as a syntax violation, the property that a body is a single region
for bindings is maintained: the only practical restriction is that
it is not possible to create mutually-recursive definitions between
distinct variable definition–expression groups, which
is necessary to ensure each letrec*-values equivalent
form in the transformed body can still be optimized.
2.6.1. Issue
The syntax and semantics of bodies described above
represent an initial attempt to find a satisfactory means of allowing
Scheme programmers to mix definitions and expressions in any order
within bodies, as requested by voters on the Stygian Blue ballot.
They correspond to a variant of semantics originally proposed by
Sergei Egorov in SRFI 251, adapted for the expansion order originally
specified by R6RS and described in the macrological fascicle. There
is also a permissive provision allowing implementations to adopt
semantics more like those of SRFI 245 or R6RS program
bodies, which do not restrict forward reference between different variable definition–expression groups. This permissive
provision is the should in the recommendation
to signal a syntax violation in the case of a reference to a variable
in a subsequent variable definition–expression group.
The working group is unsatisfied with the creation of
implicit lexical contours by variable definition–expression
groups, especially considering the behaviour of identifier
syntax defined in the body (which may inadvertently flout good
Scheme style by not behaving exactly like a variable) and the
behaviour of macros which may expand to a combination of a definition
and an expression. The R6RS program body semantics are also
problematic for the optimization-related reasons described above
in the rationale. Further, with semantics derived from those of R6RS program bodies, it would not be safely possible to use
continuations to return twice from any expression in a body before
that body’s final definition. (The R6RS encourages
implementations to raise a violation exception in this case.) Note
that the permissive provision above implies that implementations
which take advantage of it cannot impose such a restriction on
multiple return from expressions.
The working group is especially keen to receive feedback on this matter from the Scheme community.
DescriptionThe continuations of all of the non-final expressions
within a body (i.e. of the expanded expressions within the created letrec*-values equivalent) accept any number of values
and discard them. The continuation of the final expression of a
body accepts the same number of values as the continuation of the
body itself, and passes the values to that continuation. (A body
may be in tail context, in which case the continuation of the final
expression is the same as the continuation of the body itself.)