scsh-0.6/doc/src/meeting.tex

440 lines
17 KiB
TeX

\documentstyle[11pt,twoside]{article}
\input{code}
\input{latex-stuff}
\advance \textheight by 2ex
\begin{document}
\begin{center}
{\Large\bf The Scheme of Things:} \\
\vspace{2ex}
{\Large\bf The June 1992 Meeting$^{\hbox{\scriptsize 1}}$} \\
\vspace{3ex}
Jonathan Rees \\
Cornell University \\
{\tt jar@cs.cornell.edu}
\end{center}
\vspace{3ex}
\footnotetext[1]{To appear in {\em Lisp Pointers} V(4),
October--December 1992.}
An informally constituted group of people interested in the future of
the Scheme programming language met at the Xerox Palo Alto Research
Center on 25 June 1992. The main purpose of the meeting was to work
on the technical content of the next revision of the Scheme report.
We made progress on several fronts:
\begin{itemize}
\item Some differences with the IEEE Scheme standard were resolved.
\item Proposals for multiple return values and {\tt dynamic-wind} were
adopted.
\item A proposal for an {\tt eval} procedure was adopted.
\item The high-level macro facility described in the
Revised$^4$ Report's appendix will be moved into the report proper.
\end{itemize}
Two subcommittees were formed: one to work on exceptions, and one to
charter the formation of a standard library. The subcommittees will
report back to the group with proposals for inclusion in the report.
It had been hoped that there would be progress on some other fronts
(user-defined types, dynamic binding, improvements to ``rest''
parameters), but after inconclusive discussion these topics were
dropped. These topics will probably be taken up again in the future.
Norman Adams was appointed the Revised$^5$ Report's editor. It is
hoped that it will be ready by early 1993, so as to precede the
reconstitution of the IEEE standard group.
This article is my own interpretation of what transpired, and should
not be construed as definitive.
\piece{Agreement with the IEEE Scheme standard}
Until now, the Scheme reports have encouraged but not required the
empty list {\tt()} and the boolean false value {\tt\#f} to be
distinct. It has been the intent ever since the Revised Revised
Report, however, that this distinction would eventually be required.
The IEEE Scheme standard bit the bullet in 1990, and now the
Revised$^5$ report follows.
The standard also dropped the distinction between essential and
not-essential language features; most features that were formerly not
essential, such as n-ary {\tt+} and {\tt apply}, are now required.
The Revised$^5$ Report will adopt this stance, at least as regards
language features that are shared with the IEEE standard.
Non-essential non-IEEE oddities such as {\tt transcript-on} and {\tt
transcript-off} and the proposed {\tt interaction-\ok{}environment} (see
below) were not discussed at the meeting, however, and consensus on
their status will have to be reached via electronic mail.
A third aspect of the standard that was adopted was a certain obscure
paragraph regarding assignments to top-level variables (section 6,
paragraph 2). The effect of this is that if a program contains an
assignment to any top-level variable, then the program must contain a
{\tt define} for that variable; it is not sufficient that the variable
be bound. This has been the case for most variables, but the rule
applies as well to variables such as {\tt car} that have built-in
bindings. In addition, it is clarified that if a program makes such a
definition or assignment, then the behavior of built-in procedures
will not be affected. For example, redefining {\tt length} cannot
affect the behavior of the built-in {\tt list->vector} procedure.
If in some particular implementation {\tt list->vector} is written
in Scheme and calls {\tt length}, then it must be sure to call the
built-in {\tt length} procedure, not whatever happens to be the value
of the variable {\tt length}.
\piece{Multiple return values}
The {\tt call-with-values} and {\tt values} procedures were described
in an earlier Scheme of Things ({\em Lisp Pointers}, volume IV, number
1), but I'll review them here. The following is adapted from John Ramsdell's
concise description:
\begin{list}{}{}{}\item
{\tt(values \var{object} $\ldots$)}
\hfill {\rm essential procedure}
{\tt values} delivers all of its arguments to its continuation.
\vspace{2ex}
{\tt(call-with-values \var{thunk} \var{receiver})}
\hfill {\rm essential procedure}
{\tt call-with-values} calls its \var{thunk} argument with a
continuation that, when passed some values, calls the
\var{receiver} procedure with those values as arguments.
The continuation for the call to \var{receiver} is the
continuation of the call to {\tt call-with-values}.
\end{list}
Except for continuations created by the {\tt call-with-values}
procedure, all continuations take exactly one value, as now; the
effect of passing no value or more than one value to continuations
that were not created by {\tt call-with-values} is unspecified (as
indeed it is unspecified now).
{\tt values} might be defined as follows:
\begin{code}
(define (values . things)
(call-with-current-continuation
(lambda (cont) (apply cont things))))
\end{code}
That is, the procedures supplied by {\tt
call-with-current-continuation} must be passed the same number of
arguments as values expected by the continuation.
Because the behavior of a number-of-values mismatch between a
continuation and its invoker is unspecified, some implementations may
assign some specific meaning to such situations; for example, extra
values might be ignored, or defaults might be supplied for missing
values. Thus this multiple return value proposal is compatible with
Common Lisp's multiple values, but strictly more conservative than it.
The behavior of programs in such situations was a point of contention
among the authors, which is why only the least common denominator
behavior was specified.
\piece{Unwind/wind protection}
{\tt dynamic-wind}, which was described previously in this column (when it
was The Scheme Environment; {\em Lisp Pointers}, volume I, number 2),
is already implemented in many Scheme dialects. {\tt dynamic-wind}
takes three arguments, all of which are thunks (procedures of no arguments).
It behaves as if it were defined with
\begin{code}
(define (dynamic-wind before during after)
(before)
(call-with-values during
(lambda results
(after)
(apply values results))))
\end{code}
except that the execution of the {\tt during} thunk is ``protected''
against non-local entries and exits: a throw out of the execution
of {\tt during} will cause the {\tt after} thunk to be invoked, and a
throw from outside back in will cause the {\tt before} thunk to be
invoked. (By ``throw'' I mean an invocation of an explicit
continuation as obtained from {\tt call-with-current-continuation}.)
For details, the earlier Scheme Environment column refers the reader
to Friedman and Haynes's paper ``Constraining Control'' in POPL 1985,
but to save you the trouble of looking that up, I have supplied a more
direct implementation of {\tt dynamic-wind} in an appendix to the
present column.
{\tt dynamic-wind} was adopted with the following clarifications: The
semantics of {\tt(dynamic-wind \var{before} \var{during} \var{after})}
should leave unspecified what happens if a throw occurs out of {\em
before} or {\em after}\/; and it is best to defer interrupts during {\em
before} and {\em after}.
\piece{Evaluating computed expressions}
The original 1975 memo on Scheme described {\tt evaluate},
which was analogous to Lisp's traditional {\tt eval} function. {\tt
evaluate} took a single argument, an S-expression, and invoked an
interpreter on it. For example:
\begin{code}
(let ((name '+)) (evaluate (list name 2 3)))
\ev 5
\end{code}
Scheme being lexically scoped, however, there was some confusion over
which environment the expression was to be evaluated in. Should
\begin{code}
(let ((name '+))
(let ((+ *))
(evaluate (list name 2 3))))
\end{code}
evaluate to 5 or to 6?
To clarify matters, the Revised Report replaced {\tt evaluate} with
{\tt enclose}, which took two arguments, a {\tt lambda}-expression and
a representation of an environment from which to supply bindings of the
{\tt lambda}-expression's free variables. For example:
\begin{code}
(let ((name '+))
(let ((+ *))
((enclose (list 'lambda '() (list name 2 3))
(list (cons '+ +))))))
\ev 6
\end{code}
This forced the programmer to be explicit about the {\tt
lambda}-expression's enclosing environment.
For various technical and practical reasons, there was no {\tt eval}
analogue in subsequent Scheme reports. The major stumbling blocks
were how to describe {\tt eval} formally and how to define something
that makes sense in all extant variants of the language. Some Scheme
implementations contain a distinguished top-level environment, while
others extend the language by providing ways to create multiple
environments, any of which might serve equally well.
The {\tt eval} proposal adopted at the June meeting, which I reproduce
here, is one that comes from Bill Rozas.
\begin{list}{}{}{}\item
{\tt(eval \var{expression} \var{environment-specifier})}
\hfill {\rm essential procedure}
{\tt eval} evaluates \var{expression} in the environment indicated
by {\em environment-\discretionary{}{}{}specifier}. {\em
environment-specifier} may be the return value of one of the three
procedures described below, or implementation-specific extensions.
No other operations on environment specifiers are defined by this
proposal.
Implementations may allow non-expression programs (i.e.\
definitions) as the first argument to {\tt eval} \var{only} when
the second argument is the return value of {\tt interaction-environment}
or some implementation extension. In other words, {\tt eval} will never
create new bindings in the return value of {\tt null-environment} or
{\tt scheme-report-environment}.
\vspace{2ex}
{\tt(scheme-report-environment \var{version})}
\hfill {\rm essential procedure}
{\em Version} must be an exact non-negative integer corresponding to a
version of one of the Revised$^n$ Reports on Scheme. This procedure
returns a specifier for an environment that contains exactly the
set of bindings specified in the corresponding report that the
implementation supports. Not all versions may be available in all
implementations at all times. However, an implementation that
conforms to version $n$ of the Revised$^n$ Reports on Scheme must
accept version $n$. If {\tt scheme-report-environment} is
available, but the specified version is not, the procedure will
signal an error.
The effect of assigning (through the use of {\tt eval}) a variable
bound in a {\tt scheme-report-environment} (e.g.\ {\tt car}) is
unspecified. Thus the environments specified by the return
values of {\tt scheme-report-environment} may be immutable.
\vspace{2ex}
{\tt(null-environment)}
\hfill {\rm essential procedure}
This procedure returns a specifier for an environment that contains no
variable bindings, but contains (syntactic) bindings for all the
syntactic keywords defined in the report, and no others.
\vspace{2ex}
%\newpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
{\tt(interaction-environment)}
\hfill {\rm procedure}
This procedure returns a specifier for an environment that
contains imple\-men\-ta\-tion-defined bindings, typically a superset of
those listed in the report. The intent is that this procedure
will return a specifier for the environment in which the
implementation would evaluate expressions dynamically typed by the
user.
\end{list}
Rozas explains:
``The proposal does not imply the existence or support of first-class
environments, although it is compatible with them.
The proposal only requires a way of associating tags with a finite set
of distinguished environments which the implementations can maintain
implicitly (without reification).
``\,`Pascal-like' implementations can support both {\tt null-environment} and
%\penalty0
{\tt scheme-report-environment} since the environments specified by
the return values of these procedures need not share any bindings with
the current program. A version of {\tt eval} that supports these but
not {\tt interaction-environment} can be written portably,
but can be better written by the implementor, since it can share code
with the default evaluator or compiler.''
Here ``Pascal-like'' refers to implementations that are restricted to
static compilation and linking. Because an {\tt eval} that doesn't
support
\penalty0
{\tt interaction-\discretionary{}{}{}environment} can be written
entirely in the Scheme language described by the rest of the report,
it raises no troublesome questions about its formal semantics.
\piece{Macros}
The consensus of the meeting was that {\tt define-syntax}, {\tt
syntax-rules}, {\tt let-\discretionary{}{}{}syntax}, and {\tt
letrec-syntax} should be moved out of the report's appendix into the
main body of the report. Although everyone agrees that a low-level
macro facility is important, the subject is too contentious at
present, with three or more competing proposals at present. The
disposition of the rest of the appendix and of the other low-level
proposals will be left up to the report's editor.
\piece{Committee work}
There is a strong sense that some kind of exception system is needed.
However, no specific proposal was ready at the time of the meeting. A
committee has been formed to work on one. What seems to be in the
air might be described as a highly distilled version of the condition
system that Kent Pitman developed for Common Lisp. I hope that I'll
be able to report on this in a future column.
On the subject of libraries, Will Clinger's minutes report that
``the authors perceive a need to give some library official status. In
fact, we need to give official sanction to multiple libraries. There
is reason to distinguish between accepted (or standard) libraries,
experimental libraries, and proposals. The accepted libraries can
reduce the intellectual size of the language by removing things like
{\tt string->list} from the report. The experimental libraries would
contain solid implementations of experimental features, including
things that might never deserve to be in the report. The proposal
libraries could contain anything implemented in portable Scheme.''
Among the content of the accepted libraries, some features (such as
those that may be moved out of the body of the report) may be required
to be built in to implementations, while others will be expected to be
available on demand (perhaps using something similar to, but not the
same as, {\tt require} as found in Common Lisp and GNU Emacs).
A librarian was appointed (Rees), and a library committee is
developing proposals for the charter, structure, and content of the
libraries.
\separator
I would like to acknowledge Will Clinger, who prepared the minutes of
the meeting, and the various people who contributed proposals,
including Bill Rozas and John Ramsdell. Any errors here are my
responsibility, however. Thanks also to Norman Adams and Richard
Kelsey for corrections to a draft of this article.
I would also like to belatedly acknowledge Norman Adams, Pavel
Curtis, Bruce Donald, and Richard Kelsey for their comments on drafts of
my previous column.
For future columns, I am entertaining various topic possibilities,
including {\tt eval}, threads, {\tt amb}, and monads.
If you have other ideas, and particularly if you think the written
record on the language is particularly poor in certain areas, please
write and let me know.
\vspace{2ex}
%\newpage
%\bgroup \small
\piece{Appendix: An implementation of {\tt dynamic-wind}}
This program is based on my vague recollection of an ancient
manuscript by Chris Hanson and John Lamping. I apologize for the lack
of data abstraction, but the code is more concise this way.
A state space is a tree with the current state at the root. Each node other
than the root is a triple $\langle\var{before}, \var{after},
\var{parent}\rangle$, represented in this implementation as two pairs
{\tt((\var{before} .\ \var{after}) .\ \var{parent})}.
Navigating between states requires re-rooting the tree by reversing
parent-child links.
Since {\tt dynamic-wind} interacts with {\tt
call-with-current-continuation}, this implementation must replace the
usual definition of the latter.
\begin{code}
(define *here* (list #f))
\codeskip
(define original-cwcc call-with-current-continuation)
\codeskip
(define (call-with-current-continuation proc)
(let ((here *here*))
(original-cwcc (lambda (cont)
(proc (lambda results
(reroot! here)
(apply cont results)))))))
\codeskip
(define (dynamic-wind before during after)
(let ((here *here*))
(reroot! (cons (cons before after) here))
(call-with-values during
(lambda results
(reroot! here)
(apply values results)))))
\codeskip
(define (reroot! there)
(if (not (eq? *here* there))
(begin (reroot! (cdr there))
(let ((before (caar there))
(after (cdar there)))
(set-car! *here* (cons after before))
(set-cdr! *here* there)
(set-car! there #f)
(set-cdr! there '())
(set! *here* there)
(before)))))
\end{code}
%\egroup
\end{document}