[plt-scheme] verifying whether a symbol is defined at compile time?

From: Yin-So Chen (yinso.chen at gmail.com)
Date: Wed Apr 25 15:48:37 EDT 2007

Hi all -

As promised, below is my summary from studying this problem - hopefully
someone else learning scheme will find it useful ;)

The basic goal was to have a system that can allow for duplicate define
expressions within modules.  There are two identified behaviors: define-once
(subsequent defines are ignored), or define-over (subsequent define
overwrite the previous definition).

The method to recognize whether a variable has been defined is via
identifier-binding (
http://list.cs.brown.edu/pipermail/plt-scheme/2007-April/017423.html).  If
it returns anything other than 'lexical or null, then it has a top level
binding; but imported versus locally defined are separated (I later found
out that an imported binding doesn't get expanded to (#%top . binding)
during expansion -

The difference between define-once and define-over basically amounts to what
to do when a binding is identified:  define-once expands to (void) whereas
define-over expands to (set! var exp), but without additional help, neither
works across modules.

define-once: doesn't provide the variable in the requiring module

(module foo mzscheme
  (define-once x 1)
  (define-once x 2) ; (void) ignored
  (provide (all-defined)))

(module bar mzscheme
  (require foo)
  (define-once x 3) ; (void) ignored too
  (provide (all-defined))) ; provide nothing.

define-over: cannot define the variable again in the requiring module

(module foo mzscheme
  (define-over x 1)
  (define-over x 2) ; (set! x 2)
  (provide (all-defined)))
(module bar mzscheme
  (require foo)
  (define-over x 3)) ; error - cannot mutate module required variable: x

The problem with define-once is easier to solve: with the help of
bookkeeping (
http://list.cs.brown.edu/pipermail/plt-scheme/2007-April/017423.html) - one
can distinguish between whether a module has been imported vs locally
defined, and if it has only been imported once or twice.  If just the first
time, then (provide var), else (void).

The problem with define-over is harder to solve because of the way module is
designed (i.e. the boundary is fixed).  The easiest way that I know is to
prefix the module import, figure out what have been imported, and alias
them, e.g.,

(require (prefix foo: foo))
(define x foo:x) ; alias

Creating an prefix is straight forward as it can based on the module spec,
which is "unique" for the module system.

Figuring out where the module actually lives (in the file system, not in
REPL) require the use of (current-module-name-resolver), which returns a
symbol that basically maps to the file path of the module.  Translating it
back to path is straight forward.

With the actual module path one can utilize the method from
http://list.cs.brown.edu/pipermail/plt-scheme/2003-November/003698.html to
extract the variables.  Prepend the variables with the prefix, and generate
the (define var prefix:var) form for expansion.

The rest is to create the macro to expand to the above form -

This solution, of course, equates to evaluating the required modules twice,
which is probably not something that should be done without thinking.  There
could be a way to automatically retrieve the list of exported variables but
I don't know that at the moment, and it's time to solve other problems ;)

Thanks to all for help again,

On 4/22/07, Yin-So Chen <yinso.chen at gmail.com> wrote:
> Hi all -
> Just want to thank everyone (Jos, Matthew, Jens, and Eli) for providing
> the pointers - this is awesome help.  I will study the different methods and
> will definitely report back on my take aways ;)
> Very appreciated. Thanks,
> yinso
> On 4/22/07, Eli Barzilay <eli at barzilay.org> wrote:
> >
> > On Apr 22, Matthew Flatt wrote:
> > > At Sun, 22 Apr 2007 03:32:36 -0700, "Yin-So Chen" wrote:
> > > > My motivation for having a conditional define is to make it play
> > > > nice with the module system, which doesn't work well when having
> > > > multiple definitions with the same name.  I was hoping there is an
> > > > "automated" way of detecting whether a symbol is already defined
> > > > in the current module, if so, either override or not import the
> > > > definition.
> > >
> > > Within a module, `identifier-binding' should do the trick.
> >
> > Funny that this came right after the Swindle question -- they're
> > related.  When you use `defmethod' Swindle is trying to guess whether
> > you're defining a new method, or adding to an existing one.  See the
> > code in "collects/swindle/clos.ss" (look for `identifier-binding') for
> > how it's done -- my guess is that you'll want something similar.
> >
> > BUT -- having gone through that, I can tell you that you will need a
> > lot of fighting to get things right.  It's a question whether it's
> > worth it.  (In other words -- I often think that I should have not
> > included a `defmethod' -- it's much cleaner to have only `defgeneric'
> > and `add-method'.)
> >
> > --
> >           ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
> >                    http://www.barzilay.org/                 Maze is
> > Life!
> >
> --
> http://www.yinsochen.com
> ...continuous learning...

...continuous learning...
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20070425/5a9ae747/attachment.html>

Posted on the users mailing list.