On Wed, 14 Oct 2009 11:53:41 -0400, Derick Eddington
<[email protected]> wrote:
> On Wed, 2009-10-14 at 01:00 -0400, Aaron W. Hsu wrote:
>
>> [...]
>>
>> So, the module system I propose consists of two forms:
>>
>> <library> := (library [<name>] <exports> . <body>)
>> <name> := #identifier | <r6rs library name>
>> <exports> := (export <export-spec> ...)
>> <export-spec> := #identifier | (#identifier #identifier ...)
>> <body> := (#expr|#def #expr|#def ...)
>>
>>
>> In the above form, [...]
>> I have also made the name of the library
>> optional. This is to allow for anonymous modules,
>
> If <name> is optional, is this ambiguous?:
>
> (library (export foo bar) (export))
Yes this is ambiguous, and yes, I missed that.
> Is the second subform the <name> and the third subform the <exports>?
> Or, is the second subform the <exports> and the third subform the first
> form of the <body>?
>
> I think something like this disambiguates:
>
> (define (library-form-parts lib-form)
> ;; Returns three values: the library name, the exports, and the body.
> (define (named)
> (values
> (cadr lib-form)
> (cdaddr lib-form)
> (cdddr lib-form)))
> (define (anonymous)
> (values
> #F
> (cdadr lib-form)
> (cddr lib-form)))
> (if (symbol? (cadr lib-form))
> (named)
> (if (eq? 'export (caadr lib-form))
> (if (and (pair? (cddr lib-form))
> (pair? (caddr lib-form))
> (eq? 'export (caaddr lib-form)))
> (named)
> (anonymous))
> (named))))
>
> because we assume that if we get to (eq? 'export (caaddr lib-form))
> being true, the second `(export ---)' is the <exports> and is not a form
> of the <body> because `export' cannot possibly be bound yet in the scope
> of the <body>. In the very rare case it was intended that the second
> `(export ---)' be the first <body> form and that the library be
> anonymous, an unbound identifier error about `export' will not happen
> and the library will be named what was intended to be the <exports>.
> Because this case is very rare, and because I suppose this accident will
> be noticed quickly, this method of disambiguating seems acceptable, but
> I'm not certain.
I would propose instead that we force the <name> to be there, but allow it
to be #f. A #f library name means an anonymous library. I think this makes
things simpler.
>> [...]
>>
>> I have removed, in the above, the import form. This is because this
>> import
>> form should be usable anywhere, and is, in this proposed system, its own
>> form, and not a component of the library syntax.
>
> If there is not an import form as part of the library form syntax, how
> do we know what an import form in the <body> means? There would have to
> be an implicit binding of `import' to the thing which imports. But that
> would make `import' a special keyword which is always implicitly bound,
> which does not seem Schemely. What would this do?:
>
> (library (some lib)
> (export)
> (import (thing base))
> (define import 'something-else))
>
> `import' is implicitly bound at the library's top-level, so, the
> definition of another `import' conflicts with the already-bound one.
You would get an error because you have mutliple definitions of "import"
in the same scope.
> If the implicitly-always-bound `import' was considered to be implicitly
> imported, and if imported bindings could be shadowed by top-level
> definitions, then the definition of another `import' would work.
> However, allowing imported bindings to be shadowed by top-level
> definitions is not compatible with R6RS. I assume R6RS disallows such
> shadowing for good reason(s). One reason I can think of is that this
> would not be clear:
>
> (library (lib A)
> (export foo)
> (import (thing base)
> (only (lib B) foo))
> (define foo 'foo))
>
> Which `foo' is exported?
Obviously this would not work.
> If, in your proposal, imported bindings should not be shadowable by
> top-level definitions, it seems an import form must be part of the
> library form syntax.
See below, as it does not need to be, and isn't done this way in Chez
Scheme and other implementations.
> Also, with implicitly-always-bound `import', what would this do?:
>
> (library (some lib)
> (export m)
> (import (thing base)
> (thing syntax-case))
> (define-syntax m
> (lambda (stx)
> (syntax-case stx ()
> ((_ x)
> (free-identifier=? (syntax x) (syntax import)))))))
>
> ;; Outside of a library form:
> (m import) ;; #T or #F?
>
> If `import' is implicitly bound in libraries' bodies, and if outside of
> a library form it is not bound, `(m import)' will be #F. I'm not sure
> what to think about this. Though, any environment outside of library
> forms, in your proposal, I suppose would also always have `import'
> implicitly bound, and so `(m import)' will be #T.
One of the main points of my proposal is that the Library system isn't its
own "description" language that exists as wrapping all Scheme code; it
*is* Scheme code. In order to evaluate Scheme code, one must have an
environment defined already. Most Schemes I have used in the past, when
you load a given file into the REPL, or start a REPL, or otherwise start
evaluating a set of Scheme expressions and definitions, have a default
Scheme environment defined. I'm suggesting that we stick with this model,
instead of trying to wrap everything up in library forms only. In other
words, a library form is nothing more than yet another definition form
found while processing Scheme code. Implementations are free to define any
default environment that they want, and they will either provide the
library form by default, or they will allow for it to be imported through
some mechanism of their own.
Thus, at the top-level, all bindings are mutable. At the local scope
level, however, which is where all library encapsulated forms occur, you
can't have more than one definition of the same identifier, so importing
identifiers into the scope will allow them to be treated localing as
definitions, and thus, proper optimization ease easy, which I believe is
one of the primary reasons for the immutability requirement of R6RS.
If a library must be evaluated in an environment that is not a superset of
the default environment, then the IMPORT-ONLY form can be used to good
effect. In this model, R6RS programs should basically just work in most
R6RS implementations of which I know, the Script form can be retained, and
no efficiency is lost, but we have a nicer module system.
As a side note, this makes defining a REPL Semantics easier, because you
can define the top-level semantics as something compatible with REPL
implementation, and definitions loaded using LOAD can be grouped together
using BEGIN, and with a slight modification of BEGIN, we get forward
syntax references at the top-level as well, even though the semantics of
the top-level works for a REPL implementation. Sounds like a win to me.
The R6RS script model can still be retained, as it is mostly compatible
with this new interpretation, as a script can just be wrapped in an
implicit library form.
Aaron W. Hsu
--
Of all tyrannies, a tyranny sincerely exercised for the good of its
victims may be the most oppressive. -- C. S. Lewis
_______________________________________________
r6rs-discuss mailing list
[email protected]
http://lists.r6rs.org/cgi-bin/mailman/listinfo/r6rs-discuss