[racket-dev] except-out oddity

From: Eli Barzilay (eli at barzilay.org)
Date: Sun Mar 13 05:02:19 EDT 2011

About a month ago, Matthew Flatt wrote:
> At Sat, 12 Feb 2011 23:28:47 -0500, Eli Barzilay wrote:
> > Using (except-out whatever x) will omit not only `x', but also any
> > other names that `x' is provided as.  For example:
> > 
> >   (module A racket
> >     (define x 1)
> >     (provide x (rename-out [x y])))
> >   (module B racket
> >     (require 'A)
> >     (provide (except-out (all-from-out 'A) x)))
> >   (module C racket
> >     (require 'B)
> >     y) ; error
> > 
> > and it goes the other way too (excluding `y' and trying to use `x').
> > When I saw this, it seemed familiar so I looked at the docs.  My guess
> > is that this:
> > 
> >   The symbolic export name information in the latter provide-specs is
> >   ignored; only the bindings are used.
> > 
> > is saying that the above is expected.  If so, then it looks like it
> > should definitely be clarified, maybe move this sentence after the
> > example, explain the specific (common) case of excluding a name that
> > was provided with a different name, and also adding an example for
> > this?
> Ok, I'll try to clarify in the docs.
> > But at a higher level, is there a reason to not look at the name?
> I think it interacts badly with `require'- and `provide'-generating
> macros.

FWIW, here's another very confusing situation I just ran into:

   (module A racket/base
     (require racket/provide (for-syntax racket/base))
     (require (prefix-in r: r5rs))
     (provide (except-out
               (filtered-out (lambda (x) (regexp-replace #rx"^r:" x ""))
                             (all-from-out r5rs))

it really seems weird to require `r:map' as the exclusion at that
point.  Worse, if I'd do this with `racket/base' instead of `r5rs',
then this would work, but for a very obscure reason.

> > It seems bad in that the two options of
> >   1. (define x y) (provide x)
> >   2. (provide (rename-out [y x]))
> > can lead to a different result, where the second one ties the two
> > names for this kind of filtering.
> I'm not sure I understand what you mean. Do you mean that `x' is
> different from `(rename-out [y x])' when wrapped with `(except-out
> ... y)'?

No, I'm talking about a situation where you want to provide some `old'
name under a `new' name -- one way to do this is:

  (define new old) (provide new)

and another is

  (provide (rename-out [old new]))

It looks like the two are doing the same, but there's a potential
confusing problem with the second, if you get to a module that has
both names and wants to exclude only one.

> > It's also weird that the two names are the same for `except-out'
> > but in an `except-in' they're still separate.
> If I'm following, then I think it derives from the difference
> between bindings and uses and how those interact with macros.
> A variant of `except-out' that refers to external names instead of
> internal bindings may be useful, too.

That's what I think the default should be -- all of these weird cases
are a result of using the internal names where it seems that the
external ones should be used.

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

Posted on the dev mailing list.