1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
|
% This file is part of Oaklisp.
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% The GNU GPL is available at http://www.gnu.org/licenses/gpl.html
% or from the Free Software Foundation, 59 Temple Place - Suite 330,
% Boston, MA 02111-1307, USA
\chapter{Dynamic State} \label{dynamic}
As Steele and Sussman pointed out in \emph{The Art of the Interpreter},
dynamic scoping provides the most natural decomposition of state in
certain situations. This chapter describes the Oaklisp facilities for
creating and manipulating state that has dynamic extent.
\section{Fluid Variables}
\discuss{To avoid the problems that arise when fluid variables are
integrated with the lexical environment, Oaklisp fluid variables have
been placed in a completely separate dynamic environment. Fluid
variables don't even look like lexical variables, since they can only
be referenced using the \df{fluid} special form. The mechanism for
creating fluid variables is \df{bind}, which syntactically resembles
\df{let}.}
\sform{bind}{\lpar\lpar\texttt{fluid} var$_1$\rpar
val$_1$\rpar\ldots\lpar\lpar\texttt{fluid} var$_n$ val$_n$\rpar\dt body}
\doc{Evaluates \emph{body} in a dynamic environment where the $n$ symbols
are bound to the $n$ values.}
\sform{fluid}{symbol}
\doc{Returns the value of the fluid variable \emph{symbol}. Even
though \df{fluid} is a special form, it is settable, so \texttt{(set!
(fluid \emph{symbol}) \emph{value})} changes the value of the fluid
variable \emph{symbol} to \emph{value}. The reader will read \texttt{foo}
preceded by a control-v character as \texttt{(fluid foo)}; this was
motivated by the fact that control-v prints as $\bullet$ on both
Macintosh$^{\mbox{tm}}$ and Symbolics computers.}
\section{Non-local Exits} \label{sec:nonlocal}
\discuss{Most Lisp dialects include some sort of \df{catch} facility
for performing non-local exits. Oaklisp provides two facilities at
varying points on the generality vs.\ cost spectrum.}
\op{call-with-current-continuation}{operation}
\doc{Calls \emph{operation} with one argument, the current
continuation. The synonym \df{call/cc} is provided for those who feel
that \df{call-with-current-continuation} is excessively verbose.}
\index{\texttt{call/cc}|see \texttt{call-with-current-continuation}}
\sform{catch}{variable \dt body}
\doc{\emph{variable} is lexically bound to an escape operation that
may be called from anywhere within \emph{body}'s dynamic extent. If
\emph{variable} is not called, \df{catch} yields the value of
\emph{body}. This is implemented in such a way that \emph{body} is called
tail recursively.}
\sform{native-catch}{variable \dt body}
\doc{\emph{variable} is lexically bound to an escape tag that
may be thrown from anywhere within \emph{body}'s dynamic extent. If
\emph{variable} is not thrown to, \df{native-catch} yields the value of
\emph{body}. This is implemented in such a way that \emph{body} is
called tail recursively.}
\op{throw}{tag value}
\doc{Causes execution to resume at the point specified by \emph{tag}.
This point is always a \df{native-catch} expression, which immediately
yields \emph{value}. Cleanup actions specified with \df{wind-protect}
are performed while the stack is being unwound.}
\sform{wind-protect}{before form after}
\doc{\macdef{} {(dynamic-wind (lambda () \emph{before}) (lambda ()
\emph{form}) (lambda () \emph{after}))}}
\sform{funny-wind-protect}{before abnormal-before form after abnormal-after}
\doc{A \df{wind-protect} evaluates \emph{before}, \emph{form}, and \emph{after},
returning the value of \emph{form}. If \emph{form} is entered or exited
abnormally (due to \df{call/cc} or \df{catch}) the \emph{before} and
\emph{after} forms, respectively, are automatically executed.
\df{funny-wind-protect} is the same except that different guard forms
are evaluated depending on whether the dynamic context is entered or
exited normally or abnormally.}
\op{dynamic-wind}{before-op main-op after-op}
\doc{Calls the operation \emph{before-op}, calls the operation
\emph{main-op}, calls the operation \emph{after-op}, and returns the value
returned by \emph{main-op}. If \emph{main-op} is exited abnormally,
\emph{after-op} is called automatically on the way out. Similarly, if
\emph{main-op} is entered abnormally, \emph{before-op} is called
automatically on the way in.}
% \sform{unwind-protect}{form \dt unwind-forms}
% \doc{Acts like \df{block}, except that the \emph{unwind-forms} are
% guaranteed to execute even if a throw occurs out of \emph{form}. This
% construct is implemented tail-recursively, and there is no restriction on
% throws from the \emph{unwind-forms}.}
%
% \sform{unwind-protect0}{form \dt unwind-forms}
% \doc{Acts like \df{unwind-protect}, except that it yields
% the value of \emph{form}, and does not permit a tail-recursive
% implementation.}
\section{Error Resolution} \label{errors}
\discuss{\textbf{Note:} the error system is not stable, and will
probably evolve towards the Common Lisp error system, which has a
number of good ideas.}
Programs interact with the error system in three ways: they signal
various sorts of errors (typically throwing the user into the
debugger), they provide restart handlers that the user can invoke
(using \df{ret}) to escape from the debugger, and they provide
handlers to be invoked when various types of errors occur.
\subsection{Signaling Errors}
Errors are signalled using the following operations.
\op{warning}{format-string \dt format-args}
\doc{Prints out the message specified by \emph{format-string} and
\emph{format-args} and continues execution.}
\op{error}{format-string \dt format-args}
\doc{This signals \df{generic-fatal-error}, which normally has the
effect of printing out the error message specified by
\emph{format-string} and \emph{format-args} and dumping the user into a
subordinate read-eval-print loop.}
\op{cerror}{continue-string format-string \dt format-args}
\doc{This signals \df{generic-proceedable-error}, which normally has
the effect of printing the error message specified by
\emph{format-string} and \emph{format-args} and dumping the user into a
subordinate read-eval-print loop in which there is a restart handler
that continues the computation by returning a user specified value
from \df{cerror}. \emph{Continue-string} is the text associated with
this handler when it is listed as an option by the subordinate
evaluator.}
\subsection{Restart Handlers}
There are two special forms that programs can use to define more
complex restart handlers than just returning from the call to
\df{cerror}. The simpler of the two is \df{error-return}, which is
similar to \df{catch} in that it can be forced to return a value
before its body has been fully evaluated. This form is used in the
definition of \df{cerror}.
\mc{error-return}{string \dt body}
\doc{Evaluates \emph{body} in a dynamic context in which a restart
handler is available that can force the form to return. The handler
is identified by \emph{string} in the list of choices the debugger
presents to the user. If the handler is invoked by calling \df{ret}
with an argument in addition to the handler number, the
\df{error-return} form returns this value; otherwise it
returns \df{\#f}. If no error occurs, \df{error-return} yields
the value of \emph{body}.}
The second special form acts just like a \df{let} unless an error
occurs, in which case an error handler is available that re-executes
the body of the form after (possibly) rebinding the lexical variables
specified at the top of the form.
\mc{error-restart}{string \texttt{((}var$_0$
val$_0$\texttt{)}\ldots\texttt{)} \dt body}
\doc{Evaluates \emph{body} in a dynamic context in which a restart handler
is available that can force the re-evaluation of the body with new
values for \emph{var$_0$ \ldots}. These new values are specified as
additional arguments to \df{ret}. If there are not enough arguments
to \df{ret}, the remaining variables are left at their previous
values. The handler is identified by \emph{string} in the list of
choices printed by the debugger. If no error occurs,
\df{error-restart} yields the value of \emph{body}.}
\subsection{Error Handlers}
Oaklisp uses its type system to govern the resolution of errors. The
top-level environment contains a hierarchy of types which
characterizes every error that can occur. When an error condition
arises, the appropriate type is instantiated, and an error resolution
operation is performed on the new object. This operation is performed
by a method that deals with the error in a manner consistent with its
type.
There are clearly better ways of dealing with some errors than
invoking the debugger. A variety of methods have been written to deal
with the most common errors. For example, there are \df{proceed}
methods for simple arithmetic traps which substitute a program
specified value for that of the failed computation. The use of
\df{proceed} and other error resolution operations is prescribed by
the following special form.
\mc{bind-error-handler}
{\lpar\lpar err$_1$ op$_1$\rpar\ldots\lpar err$_n$ op$_n$\rpar\rpar\dt body}
\doc{Evaluates \emph{body} in a dynamic environment where the $n$
error types have been associated with the $n$ error resolution
operations. When an error occurs, the current list of condition
bindings is searched to find an operation to perform. An operation
associated with a supertype of the actual error type will be selected
if it is encountered on the list. If a suitable operation cannot be
found, the default operation \df{invoke-debugger} is performed.}
\subsection{Operations on Errors}
There are a number of operations that can be invoked on error objects
in error handlers.
\op{report}{error stream}
\doc{Instructs \emph{error} to print a descriptive message to
\emph{stream}.}
\op{invoke-debugger}{error}
\doc{This is the default error resolution operation. It is performed
on all errors unless it is explicitly overridden.}
\op{proceed}{error \dt values}
\doc{Attempts to continue from the error, eg.\ a file system error
would retry the failed operation. The \emph{values} have semantics
determined by the precise type of error. For instance, continuing a
failed attempt to open a file with a value might instruct the system
to try a new filename.}
\op{remember-context}{error after-operation}
\doc{Instructs \emph{error} to salt away the current continuation and
then call \emph{after-operation}, which should never return.}
\op{invoke-in-error-context}{error operation}
\doc{Invokes \emph{operation} on \emph{error} after moving back to the
context of the error if its been salted away.}
\subsection{Error Types}
There are a plethora of error types defined in Oaklisp.
\ty{general-error}
\doc{This is the top of the error type hierarchy. An operation
defined for \df{general-error} can be used to resolve any error.}
\ty{generic-fatal-error}
\doc{Signaled by \df{error}.}
\makin{proceedable-error}{message}
\doc{Uses \emph{message} in composing its
report.}
\ty{generic-proceedable-error}
\doc{Signaled by \df{cerror}.}
\ty{error-opening}
\doc{Various subtypes of this are signaled when various types of error
while opening files occur.}
\ty{read-error}
\doc{Subtypes of this are signaled when \df{read} sees malformed or
truncated input.}
\ty{unexpected-eof}
\doc{This subtype of \df{read-error} is signaled when the reader comes
to the end of a file unexpectedly.}
\discuss{\textbf{Work for idle hands:} Many types of errors have yet to
be implemented. For example, domain errors in arithmetic functions
generally call \df{error} rather than signaling some special variety
of error, template mismatch in the \df{destructure*} macro should
signal some special type of error rather than calling \df{cerror},
etc. Basically, most calls to \df{error} and \df{cerror} in system
level code should be replaced with \df{signal}, and appropriate
ideosyncratic types of errors should be defined, thereby giving users
more precise control over what types of system level errors to
handle.}
|