[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Scheme-reports] General comments on the draft WG1 R7 report.

Jay Reynolds Freeman scripsit:

> 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 ...

Constructive feedback is good.

> (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.

The WG, like all Scheme standardization groups before it, has implicitly
rejected this position.  If we were to adopt it, for example, the *only*
standard procedures for dealing with lists would be pair?, car, cdr, and
cons.  We standardize things because they are useful and widely accepted
without regard to how they are implemented.

> (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.

All evidence is that programs have bugs.  Specification by
implementation ends up with a stream of corrigenda or else a requirement
for bug-for-bug compatibility.

> (C) Put any optional features of any proposed major subsystem
> into optional "scheme" modules, also perhaps with source code for
> bare-bones implementations somewhere.

Note that being primitive is orthogonal to being optional.  Inexact
numbers are optional (implicitly so in R2-R5RS, explicitly so in R7RS)
but are typically implemented as primitives, since the main reason for
having them at all is performance.

> (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.)

WG2 is currently suspended until WG1 is done, partly because nobody has
the bandwidth to work on both at the same time.  So I don't expect a
catfight.  Unless the WG decides otherwise, WG2 will consist of a bunch
of modules layered over WG1, all of which are optional.  There may be
some necessary extensions to WG1 stuff as well, in the name of better
glue.  However, not all WG2 modules will be expressible in WG1 Scheme;
some will be primitive.

> 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.

The general point here, that there's a cost to adding features, is
certainly sound, especially for WG1.  However, I don't see that new
syntax is particularly worse than new procedures.  In any case, we have
added only four new syntax keywords: letrec*, parameterize, case-lambda,
and guard.

> #### 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.  

We are not in a greenfield situation here.  All Schemes have records
of some sort, and what's needed is an example of the Facade pattern,
something that can paper over the differences.  SRFI 9 is one of the
most widely supported SRFIs: of the 30 Schemes whose SRFI usage I track,
only TinyScheme, Pocket Scheme, Sizzle, and Scheme 9 do not support it.
We have made a single extension to SRFI 9, to make it generative; there
is a ticket to consider reverting this decision.

In addition, requiring a "record-ref" and "record-set!" implementation
means that Schemes which use more efficient approaches don't conform.
Because the number of fields in a SRFI-9 record is known at compile
time, it's possible to compile the getters and setters into code which
already knows the offset within the record.  This is perfectly safe as
long as the record type is checked, which must be done anyway.

> #### 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.

It's a simplification of the R6RS module system, with explicit support
for include and cond-expand.

> 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.

Crucially, a module is also a source of bindings for syntax keywords.
There is in fact no reason for R7RS modules to exist at run-time at all;
you can expand out all uses of modules by careful renaming of everything
at macro expansion time.

> 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.

There is no such list.  Every identifier used in a module must either
be defined in the module, defined in code included in the module, or
imported from another module.

> 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.

In fact, modules specify the other modules which they insist on loading,
though in R7RS this can easily be made conditional on their existence.
It is undefined whether a module is loaded more than once in this

> 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".

This implies run-time overhead that need not exist, and cannot be
extended to allow modular syntax, a very important idea.

> 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.

Again, providing a features-list would commit implementors to a run-time
solution.  Cond-expand is syntax, because the list of features is known
to the implementation in principle when it starts up, though things
like whether a module exists by name will presumably be discovered
lazily.  With cond-expand as we have specified it, it is possible to
write code that adapts smoothly to different implementations and their
non-standard modules: if we have the (gnu gnuplot) module, for example,
we can import it and conditionally include some glue, whereas if we
have the (gist) library, we can import that and conditionally include
different glue.  Different identifiers can appear free in different arms
of a cond-expand, which would not be true of a run-time solution.

> #### 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?

Raise and guard are the equivalents of try-catch and throw (almost).
We also provide with-exception-handler, which enables us to catch an
exception *before* the stack has been unwound, and raise-continuable,
which lets us return from an exception on the assumption that a handler
has dealt with it.

I hope these explanations have been helpful.  I speak only for myself.

John Cowan  <cowan@x>  http://www.ccil.org/~cowan
        Raffiniert ist der Herrgott, aber boshaft ist er nicht.
                --Albert Einstein

Scheme-reports mailing list