[R6RS] libraries

dyb at cs.indiana.edu dyb
Sun Dec 11 16:11:13 EST 2005

> > So any identifier that leaks out of a module is fair game as long as
> > the module itself exports some sort of assignment abstraction.  So,
> > I presume, this is okay too:
> >
> >   (library S "scheme://r6rs"
> >     (export A P! s)
> >     (define s '())
> >     (define-syntax A
> >       (syntax-rules ()
> >         [(_ p x) (p 's x)]))
> >     (define-syntax P!
> >       (syntax-rules ()
> >         [(_ x y) (set! x (cons y s))])))
> >   (library T "scheme://r6rs"
> >     (import S)
> >     (export P0!)
> >     (define-syntax dequote-P!
> >       (syntax-rules (quote)
> >         [(_ (quote x) e) (P! x e)]))
> >     (define (P0!) (A dequote-P! 0)))
> >
> > Again, both the set! and "reference" to s appear in the exporting module.
> Yes.
> (There's some more general problem I have with the use of literals
> here, but that doesn't have anything to do with the issue at hand.)

I see only two literals, 0 and '().  I suspect your problem is with the
form 's which looks like a literal but turns out to be the lhs of an
assignment, much as the unquoted s in the preceding version looked like
a reference but also turned out to be the lhs of an assignment.  We can
talk about addressing this general problem some other time if you like,
but unless we make radical changes to the language, there is no way
for the macro expander to determine until expansion is complete the
role(s) a programmer intends for an identifier occurrence to play,
so the problem exists and we have to deal with it.  And I believe that
the inscrutable intent of and programmer is very much the issue at hand.
We simply don't know if the programmer intended for s to be mutable via
P! and may want to err on the side of caution if we're really going to
say that only the exporting module can assign its exports.

Here's a different version of the above, one that certainly gives
me pause.

(library util "scheme://r6rs"
  (export add1 sub1 pset! count apropos)
  (define (add1 x) (+ x 1))
  (define (sub1 x) (- x 1))
  (define-syntax pset!
    (syntax-rules ()
      [(_ [x e] ...)
         (lambda (f v) (f v))
         (list (lambda (t) (set! x t)) ...)
         (list e ...))]))
  (trace-define (count x ls)
      [(memv x ls) => (lambda (ls) (add1 (count x (cdr ls))))]
      [else 0]))
  (define-syntax apropos
    (syntax-rules ()
      [(_ print) (print '(add1 sub1 count pset! apropos))])))

Like many libraries, util export an odd assortment of handy routines,
some closely related, some not.  util also exports an apropos feature
that let's us query it to see what it provides (how thoughtful!).

So now, I can write

  (import util)
  (apropos (lambda (x) (write x) (newline)))

and see what util provides:

  (add1 sub1 count pset! apropos)

and use some of the other entry points:

  (add1 3) ;=> 4
  (count 'a '(a b a c a)) ;=> 3

Unfortunately, we can also use the independent pset! and apropos features
to mess with exported variables in ways the designer surely never

(library util2 "scheme://r6rs"
  (import util)
  (export minus-count)
  (define-syntax init
    (syntax-rules (quote)
      [(_ (quote (add1 sub1 . ignore)))
       (pset! [add1 sub1] [sub1 add1])]))
  (define (minus-count x ls)
    (apropos init)
    (count x ls)))

Now if we use minus-count once:

  (import util2)
  (minus-count 'a '(a b a c a)) ;=> -3

it works fine, but look what happens to add1, sub1, and even count:

  (import util)
  (add1 3) ;=> 2
  (sub1 3) ;=> 4
  (count 'a '(a b a c a)) ;=> -3

In the earlier examples, the assignment operator and the variable
we whacked indirectly seemed related, so allowing the assignment
seemed reasonably fair.  In the current example, we're using a general
abstraction, pset!, and an independent feature, apropos, to effect an
assignment that affects another set of features, add1, sub1, and count.

This kind of thing may not arise very often, but we have to have a story
for everything that can happen, and the rarer a problem is, the more
surprising it will be when it bites.

So, while I think I can probably implement your modified semantics (both
set! and lhs id must originate in the exporting module), it's only one
of several possible interpretations of "settable only by the exporting
module", and I believe it will lead to confusion.  Your semantics
also falls short of supporting program analysis in the way that true
external immutability would, negating one of the potential benefits
of immutability.

I'm happy to compromise on the mutability issue and make module exports
immutable if all exports (direct or indirect) are totally immutable,
so that we avoid confusion and support program analysis.  Of course,
you may not need me to be happy if I end up in the minority.


More information about the R6RS mailing list