Semantic concepts

5.1  Argument and subform checking

The report requires implementations to check the arguments of procedures and subforms for syntactic forms for adherence to the specification. However, implementations are not required to detect every violation of a specification. Specifically, the report allows the following exceptions:

  1. Some restrictions are undecidable. Hence, checking is not required, such as certain properties of procedures passed as arguments, or properties of subexpressions, whose macro expansion may not terminate.

  2. Checking that an argument is a list where doing so would be impractical or expensive is not required. Specifically, procedures that invoke another procedure passed as an argument are not required to check that a list remains a list after every invocation.

  3. With some procedures, future extensions to the arguments they accept are explicitly allowed.

The second item deserves special attention, as the specific decisions made for the report are meant to enable “picky” implementations that catch as many violations and unportable assumptions made by programs as possible, while also enabling practical implementations that execute programs quickly.

5.2  Safety

R5RS describes many situations not specified in the report as “is an error”: Portable R5RS programs cannot cause such situations, but R5RS implementations are free to implement arbitrary behavior under this umbrella. Arbitrary behavior can include “crashing” the running program, or somehow compromising the integrity of its execution model to result in random behavior. This situation stands in sharp contrast to the common assumption that Scheme is a “safe” language, where each violation of a restriction of the language standard or the implementation would at least result in defined behavior (e.g., interrupting or aborting the program, or starting a debugger).

To avoid the problems associated with this arbitrary behavior, all libraries specified in the report must be safe, and they react to detected violations of the specification by raising an exception, which allows the program to detect and react to the violation itself.

The report allows implementations to provide “unsafe” libraries that may compromise safety.

5.3  Proper tail recursion

Intuitively, no space is needed for an active tail call, because the continuation that is used in the tail call has the same semantics as the continuation passed to the procedure containing the call. Although an improper implementation might use a new continuation in the call, a return to this new continuation would be followed immediately by a return to the continuation passed to the procedure. A properly tail-recursive implementation returns to that continuation directly.

Proper tail recursion was one of the central ideas in Steele and Sussman’s original version of Scheme. Their first Scheme interpreter implemented both functions and actors. Control flow was expressed using actors, which differed from functions in that they passed their results on to another actor instead of returning to a caller. In the terminology of the report, each actor finished with a tail call to another actor.

Steele and Sussman later observed that in their interpreter the code for dealing with actors was identical to that for functions and thus there was no need to include both in the language.

While a proper tail recursion has been a cornerstone property of Scheme since its inception, it is difficult to implement efficiently on some architectures, specifically those compiling to higher-level intermediate languages such as C or to certain virtual-machine architectures such as JVM or CIL.

Nevertheless, abandoning proper tail recursion as a language property and relegating it to optional optimizations would have far-reaching consequences: Many programs written with the assumption of proper tail recursion would no longer work. Moreover, the lack of proper tail recursion would prevent the natural expression of certain programming styles such as Actors-style message-passing systems, self-replacing servers, or automata written as mutually recursive procedures. Furthermore, if they did not exist, special “loop” constructs would have to be added to the language to compensate for the lack of a general iteration construct. Consequently, proper tail recursion remains an essential aspect of the Scheme language.