[plt-scheme] make-set!-transformer baffling me...
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
2
=> (inc! a2 100)
=> a2
102
=> 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
2
=> 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
#<path:/tmp/>
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!