[racket-dev] submodules

From: Eli Barzilay (eli at barzilay.org)
Date: Fri Mar 9 16:35:25 EST 2012

[Some of these points may have come up earlier, I just didn't want to
lose comments so reply as I read it.]

[To be clear in advance, the following should be qualified by an "I
love it".]

Two days ago, Matthew Flatt wrote:
> Given the term "submodule", the first thing that you're likely to try
> will work as expected:
>   #lang racket/base
>   (module zoo racket/base
>     (provide tiger)
>     (define tiger "Tony"))
>   (require 'zoo)
>   tiger
> Within `module', a module path of the form `(quote id)' refers to
> the submodule `id', if any. If there's no such submodule, then
> `(quote id)' refers to an interactively declared module, as before.

This is *really* nice in how it's building a scope for module names,
but OTOH, it makes the quote seem more like a syntactic hack...

>   [...later...]
>   (require (submod 'zoo monkey-house))
> [...] a shorthand for `(submod "." zoo)' [...]

...especially here -- why is there a quote only on the first and not
on the second?  Is there any meaning for (submod foo) without the
quote (or a sting)?  If not, then having (submod zoo monkey-house)
seems beter in building up an intuition of `submod' implicitly
"quoting" the following name.

Perhaps a more obvious question is why aren't all `submod' uses get
"." as an implicit first expression?  IIUC, this means that it works
nicely without quotes, and there's only a single magical ".." thing.

> The 'zoo module path above is really a shorthand for `(submod "."
> zoo)', where "." means the enclosing module and `zoo' is its
> submodule. You could write `(submod "." zoo monkey-house)' in place
> of `(submod 'zoo monkey-house)'.

I also love the analogy to paths -- but using strings for them doesn't
look so nice, since strings are begging to be combined...  (I can
already see myself wishing for "./../foo".)  Other than the obvious
reader issue, would there be a problem in using `|.|' and `..' for
these (and making them into special module names)?

Or if there was always an implicit first ".", then there's only need
for a single `..' magic token thing...  (It even makes syntactic sense
that (submod) is something that you can't require.)

Reading the later "Design Issues" section, I see that these are
indeed arbitrary.  So to clarify, I propose a `submod' form that
always begins with a "." (under its current semantics), with no need
to use a quote, and with `..' being a special name that goes up the
hierarchy.  To make things connect nicely to toplevel requires,
(submod foo) could also resort to the repl-entered module thing, which
means that we now have:

    (require (submod foo))

    is very common, so it can be written a bit more conveniently as

    (require 'foo)

(But this might be pushing the description into showing them as closer
than they are.)

> Instead of `require'ing its enclosing module, a `module*' form can
> use `#f' as its language, in which case its lexical context starts
> with all of the bindings of the enclosing module (implicitly
> imported) instead of with an empty lexical context. As a result, the
> submodule can access bindings of the enclosing module that are not
> exported:
>   #lang racket/base
>   (module aquarium racket/base
>     (define fish '(1 2))
>     (module* book #f
>       (append fish '(red blue))))
>   (require (submod 'aquarium book))

The explanation for this looks reasonable, but the result looks
unfortunate since the common case requires you to remember to add two
special things (the `*' and `#f').  But I also really don't like to abuse
`module' for both uses.  How about just (submodule foo ...) be a more
memorable syntax for (module* foo #f ...)?

Later, I see:

> As things stand, the ugly pattern `(module* main #f ...)'  would be
> common. Probably we should have a macro that expands to `(module* main
> #f ...)'. Should the macro be called `main'?

So above is my suggestion.  But maybe even make it more generally
convenient so it can cover the `test' use without needing a library --
taking what Jay did (I think, didn't look at the code or the
followups, yet), and making this:

  (submodule <name> E ...)

be something that can appear anywhere in the code, expands to what
(module* main #f E ...) is doing now, *and* is allowed to be used more
than once, with all uses being collected into a single submodule.

This way I can easily interleave regular code, main code, test code,
sanity assertion code, documentation code etc.

IOW, I see this as something that can be used straightforwardly with
any testing tool, or made into a macro by one, or being used in the
obvious way by in-line documentation tools like Neil V's mcfly thing
(which is more straightforward for this than srcdoc).

> A common use of `module*' is likely to be with `main', since
> `racket' will load a `main' submodule (after `require'ing its
> enclosing module) for a module named on its command line. For
> example, if you run this program via `racket':
>   #lang racket/base
>   (provide fish)
>   (define fish '(1 2))

(Just in case this was missed and is/will be in some docs: that should
probably have been '(2 1).)

> The `#lang' reader form was previously defined as a shorthand for
> `#reader' where the name after the `#lang' is mangled by adding
> "/lang/reader".  With submodules, `#lang' first tries using the name
> as-is and checking for a `reader' submodule; if it is found, then
> the submodule is used instead of mangling the name with
> "/lang/reader", otherwise it falls back to the old behavior.

(BTW, there's an obvious question here of why not do that for all
paths, so that `foo/bar/baz' can access a `bar/baz' submodule in `foo'
or a `baz' in `foo/bar'...  This is very similar to what I did in the
old plt-schme.org web page sources, where a page is usually a file,
but some files have subpages (with a `begin-page').  It was a mess.)

>   #lang racket/base
>   (provide (all-from-out racket/base)
>            fish)
>   (define fish '(1 2 3))
>   (module reader syntax/module-reader 
>     #:language 'ocean)

[This could be even nicer with:

  (module reader syntax/module-reader
    #:language (submod "."))


          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!

Posted on the dev mailing list.