[plt-scheme] make-set!-transformer baffling me...

From: Eli Barzilay (eli at barzilay.org)
Date: Tue Jan 23 04:04:33 EST 2007

On Jan 22, Jay Kint wrote:
> I'm playing around with feature expressions (still!), and I feel like
> I'm almost there, but I'm running into problem with
> make-set!-transformer.
> [...]
> reference to undefined identifer: set-quote!
> [...]
> reference to undefined identifier: set-current-directory!

You're using Swindle, and its playing dirty tricks behind your back.
I'm going to explain what happens and why it is useful, so it's going
to be verbose.  [The bottom lines: (a) I fixed it to play nicely with
these examples, (b) still, it is probably not what you want.]

Swindle is doing a generalized-set! thing -- expressions like

  (set! (foo ...) v)

expand to

  (set-foo! ... v)

for example:

  => (define a (list 1 2 3))
  => (set! (car a) 11)
  => a
  (11 2 3)

and it defines a bunch of `set-foo!'s for common `foo's, like:

  => (set! (caddr a) 33)
  => a
  (11 2 33)

Now, the way it is doing this, is by expanding the `(foo ...)'
expression first, which is how this still works when it is a macro
(`defsubst' is a convenient way to define quick macros in Swindle):

  => (defsubst (foo x) (car x))
  => (set! (foo a) 111)
  => a
  (111 2 33)

this even works with identifier macros -- and (IMO) it is useful for
cases where you want to write macros that use `set!', for example:

  => (defsubst a2 (cadr a))
  => a2
  => (inc! a2 100)
  => a2
  => a
  (111 102 33)

Now, the fact that it simply creates a `set-foo!' identifier in its
expansion looks like a hack -- but the created identifier has the same
properties as the original `foo' which works nicely -- it just means
that `set-foo!' binds occurrences of `(set! (foo ...) v)':

  => (let () (defsubst (set-+! x n m) (set! x (- m n))) (set! (+ a2 100) 102))
  => a2
  => a
  (111 2 33)
  => (set! (+ a2 100) 102)
  reference to undefined identifier: set-+!

This means that if you're using Swindle, you could re-do the pwd
example like this:

  => (defsubst pwd (current-directory))
  => (defsubst (set-current-directory! new) (pwd new))
  => (defsubst (set-current-directory! new) (current-directory new))
  => (set! pwd "/tmp")
  => pwd

Now, the problem that you're seeing, is that your set!-transformet
definition for `pwd' doesn't work -- Swindle sees:

  (set! pwd "C:/")

and as described above, it first expands `pwd' in case it's a macro --
which is true here, so we now have:

  (set! (current-directory) "C:/")

which is expanded to

  (set-current-directory! "C:/")

and without the above definition you get an error.  But this is, of
course, bogus: you defined your own expansion for (set! pwd "C:/"),
without using a `set-current-directory!' binding.  I've just committed
a fix that tests if the original `set!' form receives a
`set!-transformer', and if it is then it lets it do its thing -- so
your examples work as they do in plain MzScheme.

But still, I think that you're going in the wrong direction.
Providing a simple binding for `*features*' is not difficult, and the
common idiom is to define it as a parameter (which you can wrap with
some syntactic sugar to make it look like a settable identifier) which
plays nicely with threads.  Like I said in a previous post -- this
will actually make two separate values when used in run-time code and
in macro code.  But if you really want the CL thing, then you'll
probably end up trying to make:

  #+foo ...

work if 'foo is in `*features*'.  Implementing a `#+' reader macro
will naturally introduce yet another level, with a third instance of
the parameter, disconnected from the other two...

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

Posted on the users mailing list.