Hi all,

I've been thinking about making libraries that would generate submodules 
when they're used. However, submodules exist in a flat namespace, I'm a bit 
afraid of conflicts if I choose the same name as some other library does, 
and I don't really want users to have to supply their own local choices of 
names (`rename-in` style) since I'm thinking of these submodules as an 
implementation detail.

To be more specific about my higher-level goals, I'm thinking of 
experimenting with a system of modules that have *optional compile-time 
arguments*, which make them somewhat like ML functors. If a user requires 
the module the usual way, they get the default arguments, but they can use 
a special require spec and a system of extended module path indexes to 
supply arguments. For instance, an extended module path could represent 
"apply module X to the arguments 1 and 2, and then access the resulting 
module's Y submodule." Since the default way to require a module just gets 
its no-argument version, I'm thinking of hiding away the 
argument-processing logic in a submodule of its own.

When someone supplies these arguments to a module, what's really going to 
happen is that they're defining a local submodule and requiring it on the 
spot. After all, the compilation of that module with those arguments has to 
happen sometime, and it couldn't have happened already, so it must be 
compiled alongside the current module. A submodule represents this 
situation well.

A subtler design challenge with this idea is that a library with 
compile-time arguments probably need to stop using "generative" definitions 
of structure types, so that their types can remain stable across various 
choices of module arguments. So I'd probably supply a type definition 
mechanism that associated the defined type with a stable module path, 
similar to the way `serializable-struct` creates a submodule called `
deserialize-info`.

As you can see, if I proceed the way I'm imagining, my library is going to 
be generating submodules for several reasons. These submodules would exist 
mostly as a means to an end, so I'm not immediately inclined to expose them 
to users the way `serializable-struct` does. I probably could stabilize 
them if I put in some extra thought, but my first choice, especially early 
in development, would be to keep these details private. At least, private 
to anyone who isn't using reflective tools like  `module-compiled-submodules` 
or `current-module-name-resolver`.

In the past, I've guarded against accidental namespace conflicts by using 
gensyms as my variable names. That approach seems viable here too.

It's a little tricky to do. The name of a submodule being defined or 
required must be known at compile time, but due to Racket's separate 
compilation guarantee, different clients using my library at compile time 
will be using different instantiations of it. If my library just calls 
(gensym), those clients will all end up using different gensyms, and it 
won't work. So every instantiation of my library needs to obtain the same 
gensym, and I do that by generating a gensym one phase up and embedding it 
in a quotation, like #`(... '#,(gensym) ..). While the Racket compiler 
can't marshal every kind of 3D syntax into the compiled code, gensyms are 
one thing it actually can marshal. The gensym's unique identity seems to be 
generated again at the time it's unmarshaled, which is exactly what I want. 
Since a (non-reflective) program will unmarshal my library only once, the 
gensym will be unique to my library but shared across all my library's 
instantiations.

(See the end of this email for example code.)

Using that technique, everything works fine... at least on the command 
line. Unfortunately, in DrRacket, the submodule simply isn't found when I 
try to require it:

require: unknown module
  module name: #<resolved-module-path:(submod "/path/to/badlibrary.rkt" 
badlang-submodule-name12021)>


As far as I can tell, this error I'm getting is really specific to 
DrRacket. I tried compiling my code with more instrumentation at the 
command line using "racket -e '(compile-context-preservation-enabled #t)' 
-l errortrace -t client.rkt" but even that works successfully.

Am I simply running into a bug in DrRacket, or is this gensym technique 
obscure enough that I shouldn't rely on it? Is there a more stable 
technique that would give me a similar guarantee that my names aren't 
collision-prone? My goal with using a gensym was to avoid accidental 
incompatibilities with other code, so of course an immediate incompatiblity 
with DrRacket is a sign I might want to take a different approach.

Here are the three files of code I prepared to try this out (badlang.rkt, 
badlibrary.rkt, and client.rkt):


#lang racket
; badlang.rkt

(require (for-meta 2 syntax/parse))
(require (for-syntax racket))
(require (for-syntax syntax/parse))

(provide (all-defined-out))

; As long as we define `badlang-submodule-name` as an interned symbol
; like this, it works everywhere.
#;
(define-for-syntax badlang-submodule-name
  'private/generated-by-badlang/submodule)

; As long as we define `badlang-submodule-name` as a quoted uninterned
; symbol like this, it works at the command line but not in DrRacket.
(begin-for-syntax
  (define-syntax (define-quoted-gensym stx)
    (syntax-parse stx
      [
        (_ var:id)
        #`(define var '#,(gensym (syntax-e #'var)))]))
  (define-quoted-gensym badlang-submodule-name))

(define-syntax (define-badlang-submodule-here stx)
  #`(module #,badlang-submodule-name racket))

(define-syntax (require-badlang-submodule-from stx)
  (syntax-parse stx
    [
      (_ parent-module)
      #`(require (submod parent-module #,badlang-submodule-name))]))


#lang racket
; badlibrary.rkt
(require "badlang.rkt")
(define-badlang-submodule-here)


#lang racket
; client.rkt
(require "badlang.rkt")
(require-badlang-submodule-from "badlibrary.rkt")


-Nia

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/519167de-2f17-417c-891c-6733fceff223%40googlegroups.com.

Reply via email to