File: dynamic.tex

package info (click to toggle)
oaklisp 1.3.7-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 5,776 kB
  • sloc: ansic: 4,014; makefile: 149
file content (289 lines) | stat: -rw-r--r-- 12,482 bytes parent folder | download | duplicates (5)
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.}