@node Control Mechanisms
@chapter Controlling the Flow of Program Execution
+@menu
+* begin:: Evaluating a sequence of expressions.
+* if cond case:: Simple conditional evaluation.
+* and or:: Conditional evaluation of a sequence.
+* while do:: Iteration mechanisms.
+* Continuations:: Continuations.
+* Multiple Values:: Returning and accepting multiple values.
+* Exceptions:: Throwing and catching exceptions.
+* Error Reporting:: Procedures for signaling errors.
+* Dynamic Wind:: Guarding against non-local entrance/exit.
+* Frames:: Another way to handle non-localness
+* Handling Errors:: How to handle errors in C code.
+@end menu
+
Scheme has a more general view of program flow than C, both locally and
non-locally.
of function activations without using the normal call or return
operations.
-[ XXX - tail calls instead of goto. ]
+The primitive means of C for local control flow is the @code{goto}
+statement, together with @code{if}. Loops done with @code{for},
+@code{while} or @code{do} could in principle be rewritten with just
+@code{goto} and @code{if}. In Scheme, the primitive means for local
+control flow is the @emph{function call} (together with @code{if}).
+Thus, the repetition of some computation in a loop is ultimately
+implemented by a function that calls itself, that is, by recursion.
+
+This approach is theoretically very powerful since it is easier to
+reason formally about recursion than about gotos. In C, using
+recursion exclusively would not be practical, tho, since it would eat
+up the stack very quickly. In Scheme, however, it is practical:
+function calls that appear in a @dfn{tail position} do not use any
+additional stack space.
+
+A function call is in a tail position when it is the last thing the
+calling function does. The value returned by the called function is
+immediately returned from the calling function. In the following
+example, the call to @code{bar-1} is in a tail position, while the
+call to @code{bar-2} is not. (The call to @code{1-} in @code{foo-2}
+is in a tail position, tho.)
+
+@lisp
+(define (foo-1 x)
+ (bar-1 (1- x)))
+
+(define (foo-2 x)
+ (1- (bar-2 x)))
+@end lisp
+
+Thus, when you take care to recurse only in tail positions, the
+recursion will only use constant stack space and will be as good as a
+loop constructed from gotos.
+
+Scheme offers a few syntactic abstractions (@code{do} and @dfn{named}
+@code{let}) that make writing loops slightly easier.
+
+But only Scheme functions can call other functions in a tail position:
+C functions can not. This matters when you have, say, two functions
+that call each other recursively to form a common loop. The following
+(unrealistic) example shows how one might go about determing whether a
+non-negative integer @var{n} is even or odd.
+
+@lisp
+(define (my-even? n)
+ (cond ((zero? n) #t)
+ (else (my-odd? (1- n)))))
+
+(define (my-odd? n)
+ (cond ((zero? n) #f)
+ (else (my-even? (1- n)))))
+@end lisp
+
+Because the calls to @code{my-even?} and @code{my-odd?} are in tail
+positions, these two procedures can be applied to arbitrary large
+integers without overflowing the stack. (They will still take a lot
+of time, of course.)
+
+However, when one or both of the two procedures would be rewritten in
+C, it could no longer call its companion in a tail position (since C
+does not have this concept). You might need to take this
+consideration into account when deciding which parts of your program
+to write in Scheme and which in C.
In addition to calling functions and returning from them, a Scheme
program can also exit non-locally from a function so that the control
@code{call-with-current-continuation}. Guile also offers a slightly
restricted set of functions, @code{catch} and @code{throw}, that can
only be used for non-local exits. This restriction makes them more
-efficient. Error reporting (with the function @code{error}) is done by
-invoking @code{throw}, for example. The functions @code{catch} and
-@code{throw} belong to the topic of @dfn{exceptions}.
+efficient. Error reporting (with the function @code{error}) is
+implemented by invoking @code{throw}, for example. The functions
+@code{catch} and @code{throw} belong to the topic of @dfn{exceptions}.
Since Scheme functions can call C functions and vice versa, C code can
-experience the more general flow of control of Scheme as well. It is
+experience the more general control flow of Scheme as well. It is
possible that a C function will not return at all, or will return more
than once. While C does offer @code{setjmp} and @code{longjmp} for
-non-local exits, it is still a unusual thing for C code. In contrast,
-non-local exits are very common in Scheme, mostly to report errors.
+non-local exits, it is still an unusual thing for C code. In
+contrast, non-local exits are very common in Scheme, mostly to report
+errors.
You need to be prepared for the non-local jumps in the control flow
whenever you use a function from @code{libguile}: it is best to assume
when it is exited non-locally. Likewise, the port needs to be set again
when control enters non-locally.
-Scheme code can use the @code{dynamic-wind} function to arrange the
-setting and resetting of the global state. C code could use the
+Scheme code can use the @code{dynamic-wind} function to arrange for
+the setting and resetting of the global state. C code could use the
corresponding @code{scm_internal_dynamic_wind} function, but it might
-prefer to use the @dfn{frames} concept that is more natural for C code.
-
-@menu
-* begin:: Evaluating a sequence of expressions.
-* if cond case:: Simple conditional evaluation.
-* and or:: Conditional evaluation of a sequence.
-* while do:: Iteration mechanisms.
-* Continuations:: Continuations.
-* Multiple Values:: Returning and accepting multiple values.
-* Exceptions:: Throwing and catching exceptions.
-* Error Reporting:: Procedures for signaling errors.
-* Dynamic Wind:: Guarding against non-local entrance/exit.
-* Frames:: Another way to handle non-localness
-* Handling Errors:: How to handle errors in C code.
-@end menu
+prefer to use the @dfn{frames} concept that is more natural for C
+code.
@node begin
@cindex sequencing
@cindex expression sequencing
-@code{begin} is used for grouping several expressions 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:
+The @code{begin} syntax is used for grouping several expressions
+together so that they 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:
@lisp
(if (> x 0)
scm_frame_begin (0);
c_s1 = scm_to_string (s1);
- scm_frame_unwind (free, c_s1, SCM_F_EXPLICIT);
+ scm_frame_unwind (free, c_s1, SCM_F_WIND_EXPLICITLY);
c_s2 = scm_to_string (s2);
- scm_frame_unwind (free, c_s2, SCM_F_EXPLICIT);
+ scm_frame_unwind (free, c_s2, SCM_F_WIND_EXPLICITLY);
c_res = foo (c_s1, c_s2);
if (c_res == NULL)
reentered with a captured continuation. When you are prepared to handle
reentries, include @code{SCM_F_FRAME_REWINDABLE} in @var{flags}.
+Being prepared for reentry means that the effects of unwind handlers
+can be undone on reentry. In the example above, we want to prevent a
+memory leak on non-local exit and thus register an unwind handler that
+frees the memory. But once the memory is freed, we can not get it
+back on reentry. Thus reentry can not be allowed.
+
+The consequence is that continuations become less useful when
+non-reenterable frames are captured, but you don't need to worry about
+that too much.
+
The frame is ended either implicitly when a non-local exit happens, or
explicitly with @code{scm_end_frame}. You must make sure that a frame
-is indeed ended properly. If you fail to call @code{scm_end_frame} each
-@code{scm_begin_frame}, the behavior is undefined.
+is indeed ended properly. If you fail to call @code{scm_end_frame}
+for each @code{scm_begin_frame}, the behavior is undefined.
@end deftypefn
@deftypefn {C Function} void scm_frame_end ()
ends explicitly with @code{scm_frame_end}.
The function @code{scm_frame_unwind_with_scm} takes care that @var{data}
-is protected from garbage collected.
+is protected from garbage collection.
@end deftypefn
@deftypefn {C Function} void scm_frame_rewind (void (*func)(void *), void *data, scm_t_wind_flags flags)
as well.
The function @code{scm_frame_rewind_with_scm} takes care that @var{data}
-is protected from garbage collected.
+is protected from garbage collection.
@end deftypefn