I am Jay Reynolds Freeman (no relation to "Saurik"). I am the implementor
of two R5 Schemes. They are Wraith Scheme (open-source/shareware, for the
Macintosh) and Pixie Scheme III (open-source/cheap in the App Store, for the
iPad). I have been implementing Schemes and making them available for, good
grief, almost 25 years. I have a number of comments to make about the R7 draft
report, and have been wondering how to jump in, and have decided that a timely
belly-flop is better than a graceful dive too late, so here goes ...
So you will know where I am coming from, I voted against R6 because I
thought it was too complicated for me to implement, and I have not tried to
implement it for that same reason. I am not opposed to anything in R6 in
principle, nor am I saying it is too much for one person to implement, I am
merely saying that it is too much for *me*.
I agree with recent comments here about the difficulties of simultaneously
(1) making WG1 R7 simple in its own right, (2) making it upward-compatible --
or at least not disastrously incompatible -- with whatever WG2 comes up with,
and (3) being judicious about when, how and how much to be backward-compatible
with R6 and/or R5. I commend all the WG1 members for not having simply hidden
under the bed till the dust has settled. I have a few concerns about (1) and
(3), and I hope I have constructive suggestions as well. I can't speak much to
(2) because I have no idea what WG2 is up to, and I cannot speak much to (3)
because I suspect the main difficulties are where R5 did it one way and R6 did
it another way, and as I have indicated, my eyes rather glazed over when I saw
the size of the R6 report.
I have particular concerns about the record system, about the module
system, and about exception handling (and let me say that none of my Scheme
implementations have a record system, a module system, or exception handling --
I am not trying to sneak in my own work under the guise of constructive
criticism). In any case, let me start with some generalities. I think that a
good strategy for keeping things simple would be:
(A) For any proposed major subsystem of WG1 R7, require as built-ins to the
language no more than a set of constructs which cannot easily be written in the
rest of R7 Scheme itself -- these would be things that implementors would have
to create in, e.g., the C++ code of the implementation itself.
(B) Put any other necessary features of any proposed major subsystem into a
required "scheme" module of the language, and provide -- either as an appendix
or by a link to a repository somewhere -- Scheme source for a bare-bones,
minimalist implementation of that module. (There is plenty of precedent here;
I have in mind the derived expression types in section 7.3 of R5, and the
example code for "delay" and "promise" in section 6.4 of R5. Also, many SRFIs
could serve as guides for how this kind of approach would work.)
(C) Put any optional features of any proposed major subsystem into optional
"scheme" modules, also perhaps with source code for bare-bones implementations
somewhere.
(D) In a perfect world, large portions of the difference between WG1 R7 and
WG2 R7 might thus merely be that modules that were optional in the former were
required in the latter. (In an imperfect world there would be a cat fight
between WG1 and WG2.)
Also with respect to simplicity, I am rather in favor of keeping new
syntactic constructs to a minimum. Although it is true that "define-syntax"
and its friends make it easy enough to implement new syntax, nevertheless, each
item of new syntax is one more thing for users to remember, and is usually lots
and lots more things for implementors to test: The ease of adding new
constructs should not blind us to how bewildering they can look to the
uninitiated, and to how much longer, fatter, fussier and harder to maintain
they make the implementors' regression suites.
#### About the record system: In that context, noting that the WG1 R7
record system appears to have grown out of SRFI 9, it looks to me as if a
reasonable bare-bones requirements for built-ins would be (a) a record
abstraction, together with (b) procedures "record?", "make-record",
"record-ref" and "record-set!" ("record" -- analagous to "vector" or "string"
-- might also be useful), all as defined in SRFI #9. As SRFI #9 points out,
"record" is a simple variant on "vector", so should be easy to install in most
implementations. With these primitives built in, any implementor could use
just the rest of the the SRFI #9 code to get a required "record" module going,
and anyone -- implementor or user -- could write a better (or at least
different) version of the required "record" module, with some expectation that
it would be portable among implementations.
#### About the module system: I have two worries about the module system,
but they are related. (I have the feeling there is a lot of history about how
the "module" system is set up to work; I apologize for not knowing where it
came from.) The WG1 R7 module system is novel in that it seems to go to great
lengths to specify just how the code that implements a module should be
written. Doing so means, I think, that it defines more functionality than is
strictly necessary, in order to support the requirements for specifying the
internals.
A key point in thinking about modules is that they may be written both by
users and by implementors or Scheme wizards. Their intent may range from
simple one-off use by a user to widespread distribution over many
implementations. Thus some freedom in how to go about implementing modules
might be useful.
It seems most straightforward to me to think of a module as an opaque
object -- let's call it a procedure for the sake of a definite example -- that
has certain behaviors: Basically, a module is a source of bindings for certain
procedures and the like. As a convenience (and perhaps as an aid for reading
documentation and for looking up specific bindings within itself) it provides a
list of the default symbols that its contents might reasonably be expected to
be bound to in common situations. It must also have some means to know that
other modules upon which it may depend have been loaded, so that it can import
any bindings from them that it may need. The meaning of that last is that if
modules foo and bar depend on each other, then since these two modules may not
know the location of each others' source files, presumably what one does is
something like this:
load the file for module foo
load the file for module bar
tell foo to load in what it needs from
other modules (just bar in the present case)
tell bar to load in what it needs from
other modules (just foo in the present case)
With these comments in mind, I can give an informal example of how to
specify a module system more simply, more or less as follows (many details TBD):
A module is a procedural object that responds to keywords (symbols) in much
the manner of a class with methods. There are three such keywords, here
illustrated informally by example, in the case in which the "scheme 'complex'
module" is represented by a procedural object named "complex".
(complex 'exports)
==> (angle magnitude imag-part real-part make-polar make-retangular)
(complex 'import 'imag-part)
==> <the procedural code that you would expect for "imag-part">
So that if you didn't like abbreviations, you could write
(define imaginary-part (complex 'import 'imag-part))
Now suppose you had two modules "foo" and "bar" with mutual dependencies. In
that case what you would do is
(load <file for "foo">) ==> defines a module bound to "foo"; e.g.,
contains source code like "(define (foo keyword . rest) ...)"
(load <file for ""bar>) ==> defines a module bound to "foo".
Then subsequently -- here is the third keyword, "'close" in the sense of
closure.
(foo 'close)
(bar 'close)
On receipt of 'close, you can imagine code running somewhere inside of "foo"
that does something like:
(set! local-value-for-bar-1 (bar 'import 'bar-1))
in which "local-value-for-bar-1" had been defined in a let somewhere inside the
source for "foo". (In many cases, it would just be "(set! bar-1 (bar 'import
'bar-1)", I am merely making the point that it is possible to use local names
if need be.)
With all modules defined as providing this basic functionality, it would
not be hard to have optional modules, with bare-bones source-code
implementations provided, that extended it, by providing (eg) more conventional
procedural interfaces to import things, and perhaps providing procedures or
macros to create modules.
If modules were defined in terms of functionality this way, there would be
many ways to implement them, and I can imagine that different users, with
different intents, might well implement them in different ways. (I did cook up
a straw-man implementation before posting this note, and will provide it on
request if anyone wants to see it.)
Before I leave modules, let me consider "cond-expand". It is clearly based
on a SRFI, and that is perhaps a case for leaving it alone, but how about
instead requiring each implementation to provide a functionally-accessed alist
of features it provided, perhaps as the procedure call (features), which would
return, e.g., ((r7rs) (exact-closed) (ratios) (name my-scheme-implementation)
...). (Such a list might even be alterable in case a module or something added
to it.) Users could then use "assv" and the like on the alist and feed the
results directly to cond -- there would be no need for a new syntactic
construct.
#### About exception handling: My worries here are a little vaguer, not
least because I find the discussion of exception handling in the draft report
sufficiently confusing that I am not sure what it all does. (Mea culpa, I am
sure I will figure it out somehow.) Yet still I think there is too much stuff
here; at least, too much built in. Java seems to get along with just "try",
"catch" and "throw". Do we really need more mandatory stuff in a language that
is supposed to be simple?
I hope everybody takes these comments as constructive, and I also hope they
are timely. And once again I say that though I do have preferences, none of
the approaches here are based on anything in my own implementations; I am not
trying to sneak in my own work or make it easier for me to morph Wraith Scheme
and Pixie Scheme III into R7 by having R7 adopt what I have done.
-- Jay Reynolds Freeman
---------------------
[email protected]
http://web.mac.com/jay_reynolds_freeman (personal web site)
_______________________________________________
Scheme-reports mailing list
[email protected]
http://lists.scheme-reports.org/cgi-bin/mailman/listinfo/scheme-reports