compile: unbound variable in module (in the transformer environment,
which does not include the macro definition that is visible to run-
time expressions)
Do you know what does it mean? I am defining a helper macro to define
transformers. I wonder if I am accidentally relying on features
specific to Ikarus, if it is issue related to phase separation, or
what else :-(
For the curious, the code is here:
https://www.phyast.pitt.edu/~micheles/scheme/sweet-macros.sls
> Do you know what does it mean? I am defining a helper macro to define
> transformers. I wonder if I am accidentally relying on features
> specific to Ikarus, if it is issue related to phase separation, or
> what else :-(
MzScheme does not like it if you do:
(define-syntax foo (lambda (stx) ---))
(define-syntax bar (lambda (stx) --- foo ---))
while Ikarus allows it. Ikarus says: hey, we have the transformer,
so, we might as well make it available. MzScheme says: hey, if you
can't access run-time variables, then you can't access run-time
macros either.
To make it work under MzScheme, you have to split this library into
two (or maybe more) libraries, and add the "import for" clauses that
make MzScheme happy.
Aziz,,,
Damn, this is the answer I did not want to hear!
Anyway, it seems I can get it to work by following the examples in
http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-10.html#node_sec_7.2 .
I am still curious, though. I would like to understand the technical
reason why Ikarus is able to recognize the helper macro without
forcing me to put it in another module, whereas mzscheme cannot. It
seems that in Ikarus can go away without
the (import (for (module) expand)) trick, but why it is so?
I suppose the difference to be somewhat related to the fact that
Ikarus is an incremental compiler whereas mzscheme is a batch
compiler, but I must admit that this point is quite foggy to me.
Care to explain?
Michele Simionato
Michele: Does mzscheme in your post mean R6RS language of mzscheme?
If not, I think you can use:
(define-for-syntax foo (lambda (stx) ---))
(define-syntax bar (lambda (stx) --- foo ---))
However, if you need a full explanation, send a post to the PLT
mailing list in order for Flatt to see the question.
--
Jens Axel Søgaard
What happens in the following situation:
Library A:
(define-syntax foo (lambda (stx) ---1))
Library B imports A:
(define-syntax foo (lambda (stx) ---2))
(define-syntax bar (lambda (stx) --- foo ---))
Will PLT and Ikarus behave the same here?
--
Jens Axel Søgaard
Yes, I am running
$ plt-r6rs --install sweet-macros.sls
and I have already posted the code I want to port:
http://www.phyast.pitt.edu/~micheles/scheme/sweet-macros.sls
This morning I did some experiment with a simplified version
of the code, by putting many libraries in the same file and using
(import for expand) and it worked, but I cannot get the real code to
compile now. I cannot work on it this evening, so I will leave it for
tomorrow. If somebody who is familiar with the PLT build system wants
to try, you are welcome: the module contains only fifty lines and four
macros, no helper functions at all, so it should not be a big deal.
I also run the Ikarus version on ypsilon, but it did not
work due to a bug in the recognition of patterns of the form (...
xxx). Actually Aziz found a workaround by replacing each ...
with (... ...), but it would be best if the bug was fixed.
I will send a bug report to the tracket tomorrow.
This code is for a good cause, to divulgate Scheme to the masses, so
feel free to help ;)
M. Simionato
Update: I was able to make the code to run both with Ikarus and PLT,
by splitting it into three files. For the curious, here it is:
http://www.phyast.pitt.edu/~micheles/scheme/sweet-macros.zip.
Notice: I did not perform any non-trivial tests in PLT, so I am not
really sure it works as it should.
> Update: I was able to make the code to run both with Ikarus
> and PLT, by splitting it into three files.
The single library version was more sweet IMHO. As if writing
syntax-case macros was not hard enough. Pop quiz for all: are
all the specified "for --- run expand" phases in all libraries
needed, or are some superfluous? Justify for exact credit.
Aziz,,,
I am nearly sure some are superfluous, but I did not want to spend
more time fighting with PLT Scheme phase separation worries. You know,
I have a day job too! ;)
No:
[d@eep:~/.plt-scheme/4.1.2.2/collects]-> cat A/main.sls
#!r6rs
(library (A)
(export foo)
(import (rnrs))
(define-syntax foo
(syntax-rules ()
[(_) "A:foo"])))
[d@eep:~/.plt-scheme/4.1.2.2/collects]-> cat B/main.sls
#!r6rs
(library (B)
(export bar)
(import (rnrs) (for (A) expand))
(define-syntax foo
(syntax-rules ()
[(_) "B:foo"]))
(define-syntax bar
(lambda (_)
#`'(B:bar #,(foo)))))
[d@eep:~/.plt-scheme/4.1.2.2/collects]-> cat ~/t10/use-A-B.sps
#!r6rs
(import (rnrs) (B))
(write (bar)) (newline)
[d@eep:~/.plt-scheme/4.1.2.2/collects]-> mzscheme ~/t10/use-A-B.sps
(B:bar "A:foo")
[d@eep:~/.plt-scheme/4.1.2.2/collects]-> ikarus --r6rs-script ~/t10/
use-A-B.sps
Unhandled exception:
Condition components:
1. &who: foo
2. &message: "multiple definitions of identifier"
3. &syntax:
form: foo
subform: #f
4. &source-information:
file-name: "./B/main.sls"
character: 88
5. &trace: #<syntax foo (char 88 of ./B/main.sls)>
[d@eep:~/.plt-scheme/4.1.2.2/collects]->
--
: Derick
----------------------------------------------------------------
Or just rename the imported 'foo'. Problem solved :)
I'll bite:
The single-library version was more messy/confusing IMHO. As if
writing `syntax-case' macros was not hard enough. Pop quiz: the
question that Jens asked (plus a bunch of other situations where
state is involeved and/or where some bindings are mentioned at
multiple levels). Justify for extra credit.
--
((lambda (x) (x x)) (lambda (x) (x x))) Eli
Barzilay:
http://www.barzilay.org/ Maze is
Life!
Sorry Eli, that was not the correct answer. (It was actually not
an answer at all. You shouldn't answer questions with questions).
Aziz,,,
Regardless of phasing semantics, R6RS specifies the following:
Quote from R6RS:
An identifier can be imported with the same local name from two or more
libraries or for two levels from the same library only if the binding
exported by each library is the same (i.e., the binding is defined in
one library, and it arrives through the imports only by exporting and
re-exporting). Otherwise, no identifier can be imported multiple times,
defined multiple times, or both defined and imported. No identifiers are
visible within a library except for those explicitly imported into the
library or defined within the library.
End of Quote
So, the you cannot have foo both defined in a library and at the same
time imported into that library. Similarly, you cannot have two
different foos defined in two different libraries imported into the
same place, regardless of phasing.
E.g., if you have:
(library (A) (export x) (import (rnrs)) (define x ---))
and
(library (B) (export x) (import (rnrs)) (define x ---))
then the following is not a valid library definition:
(library (C) (export) (import (for (A) run) (for (B) expand)))
And indeed, it fails under all of Ikarus, Larceny, and Ypsilon
with a clear description of the error.
You need to confirm with Matthew whether this behavior of mzscheme
was intended or just an oversight.
Aziz,,,
(I leave that to DrScheme's "check syntax" button, which highlights in
red unused requires.)
> (It was actually not an answer at all. You shouldn't answer
> questions with questions).
Q: Why does a Jew answer a question with a qiestion?
A: Why not?
I was a bit unclear on how B imports A (because I didn't
think the question through).
Let's try a few examples in the "Pretty Big" language.
First let's try Derrick's example, which gave the
provoked the exception "multiple definitions of identifier"
in Ikarus.
(module a scheme
(provide foo)
(define-syntax foo (syntax-rules () [(_) "A:foo"])))
(module b scheme
(require-for-syntax 'a)
(provide foo bar)
(define-syntax foo "b:foo")
(define-syntax bar (lambda (_) #`'(b:bar #,(foo)))))
(require 'b)
(bar)
expand: unbound identifier in module (in the transformer
environment, which does not include the macro definition
that is visible to run-time expressions) in: foo
(the use of foo i.e. (foo) is colored red)
Now let's try importing A in a different phase:
(module a scheme
(provide foo)
(define-syntax foo (syntax-rules () [(_) "A:foo"])))
(module b scheme
(require 'a)
(provide foo bar)
(define-syntax foo "b:foo")
(define-syntax bar (lambda (_) #`'(b:bar #,(foo)))))
(require 'b)
(bar)
This gives the error:
module: identifier is already imported in: foo
(the foo in module B is colored red)
Finally let's import A with (for-meta 1 a)
(module a scheme
(provide foo)
(define-syntax foo (syntax-rules () [(_) "A:foo"])))
(module b scheme
(require (for-meta 1 'a))
(provide foo bar)
(define-syntax foo "b:foo")
(define-syntax bar (lambda (_) #`'(b:bar #,(foo)))))
(require 'b)
(bar)
This runs and gives:
(b:bar "A:foo")
What I was hoping was to find a situation similar to:
> MzScheme does not like it if you do:
> (define-syntax foo (lambda (stx) ---))
> (define-syntax bar (lambda (stx) --- foo ---))
> while Ikarus allows it. Ikarus says: hey, we have the transformer,
> so, we might as well make it available. MzScheme says: hey, if you
> can't access run-time variables, then you can't access run-time
> macros either.
Since the first example gave this error,
expand: unbound identifier in module (in the transformer
environment, which does not include the macro definition
that is visible to run-time expressions) in: foo
(the use of foo i.e. (foo) is colored red)
I think the conclusion is that MzScheme detects the situation,
and thus Matthew deliberately chose this behaviour. But why?
I think it was an oversigt, due to the error messages in the
first two examples above.
--
Jens Axel Søgaard
> Q: Why does a Jew answer a question with a qiestion?
>
> A: Why not?
Sorry, not allowed to laugh.
>> You need to confirm with Matthew whether this behavior of mzscheme
>> was intended or just an oversight.
>
> I think it was an oversigt, due to the error messages in the
> first two examples above.
Fixed in SVN.
--
Jens Axel Søgaard
JTMI, this example showed ikarus resolving a library (A) to the file ./
A/main.sls , but this is experimental and might change.
--
: Derick
----------------------------------------------------------------
Too bad. It's not racist joke and genuinely fun in a puerile
way... :)
Thanks Jens for the update. I hope this suffices as an answer to Eli.
Aziz,,,
(Of course not, I didn't ask any real question...)
Sorry. It was only an incomplete sentence fragment that I incorrectly
mistook for a question in the form of a pop quiz. My bad.
Aziz,,,
(*sigh* My guess is that you know all that, but since you keep
pulling my tongue...)
If you really need it spelled out: I think that the lack of phase
separation, or rather the intentional phase-mis-separation is not a
good idea. What you semi-jokingly described with "hey, we have the
transformer, so, we might as well make it available" is -- IMO -- a
mistake much like Perl's "hey, we know how to print the number we
might as well make it usable as a string". However, I think that in
the phase case the error is more severe: I see the two phases as
completely separate worlds, with completely separate semantics, and
the fact that some of the names coincide should not make that any
different. (And it's more than just seeing it: I'm actually using
this feature.)
You also said "As if writing syntax-case macros was not hard enough"
-- which is a good point: writing algorithms is a tough job, adding
weird puns like making numbers behave like string helps write shorter
code -- but it certainly does not help write code. It's basically the
language trying to second guess what you're trying to say, in a way
that is "mostly obvious enough" that you won't get any nasty surprises
-- and when done at the level of bindings, it can be much more
dangerous. In other words, the lack of "hey, we have a binding" kind
of guesses, and the explicit in-code mention of which bindings go in
which phase are exactly features that *help* write `syntax-case'
macros.
After all, the body of a `define-syntax' *is* in a different world
whether you like it or not (or else you must forbid separate
compilation), and if it's in a different world, pretending that it
isn't by guessing which binding goes where can make the code a little
shorter, but at the expense of hopelessly confusing some people. This
thread started from a confusion over it, from someone who seems
experienced enough to quickly get what's going on; how hard would a
`define-syntax' newbie crash? Any kind of beginner course that I have
seen (which is much more than htdp-style courses) is making things
explicit for newbies, because it helps to master things explicitly
before you can deal with a "usually-fine" implicit guess.
-- So, "as if writing syntax-case macros was not hard
enough"... exactly! -- Why complicate their lives with implicit
guessing?
As for R6RS modules being forbidden from having the same name exist at
two levels if it doesn't come from the same source... Well... As
someone once said "R6RS is \"perfect\".".
I wouldn't be able to pull your tongue had it not extended this far.
Your tongue aside ...
> If you really need it spelled out: I think that the lack of phase
> separation, or rather the intentional phase-mis-separation is not a
> good idea. What you semi-jokingly described with "hey, we have the
> transformer, so, we might as well make it available" is -- IMO -- a
> mistake much like Perl's "hey, we know how to print the number we
> might as well make it usable as a string".
First off, your argument goes like "X is like Y (pulling inapplicable
analogy out of the blue), but Y is bad, so X must be bad". This is
like saying "phish (the band) sounds a lot like fish, but fish stinks
so I won't go to the concert". So, Perl aside, ...
> However, I think that in
> the phase case the error is more severe: I see the two phases as
> completely separate worlds, with completely separate semantics, and
> the fact that some of the names coincide should not make that any
> different.
Well, we are not entitled to your view of the world. In my view
(which you're not obliged to follow), an imported identifier (say
lambda, cond, foo, or whatever), means exactly the same thing at
all times. Is this more confusing than your view?
> (And it's more than just seeing it: I'm actually using this feature.)
I hope this is not implying that my "feature" is useless, because
I use my feature too, you know.
> You also said "As if writing syntax-case macros was not hard enough"
> -- which is a good point: writing algorithms is a tough job, adding
> weird puns like making numbers behave like string helps write shorter
> code -- but it certainly does not help write code. It's basically the
> language trying to second guess what you're trying to say, in a way
> that is "mostly obvious enough" that you won't get any nasty surprises
> -- and when done at the level of bindings, it can be much more
> dangerous.
Your Perl analogies aside again ...
Who is "second guessing" what? And what's this nasty surprising
danger that you speak of? Are you talking about nuclear missiles
flying out of my expand-time environment? (because that would be
funny)
Sounds like you're trying to spread fear, uncertainty, and doubt
about my system.
> In other words, the lack of "hey, we have a binding" kind
> of guesses, and the explicit in-code mention of which bindings go in
> which phase are exactly features that *help* write `syntax-case'
> macros.
Crap. Sorry. It doesn't for me. Or else I would've done something
similar in Ikarus. As a matter of fact, the whole point of Ikarus's
implementation of libraries is to eliminate such "help"; thank you
very much.
> After all, the body of a `define-syntax' *is* in a different world
> whether you like it or not
NO! Stop here!
Syntax definitions are expanded and evaluated in left-to-right order
in which they appear in an definition context. That means, that at
*expand time*, at the point [*] of the library below, the right-hand-
side expression of the foo syntax definition is already expanded,
evaluated, and *is* in the expand-time environment.
(library (acme)
(export ---)
(import ---)
(define-syntax foo ---)
; [*]
(define-syntax bar --- (foo) ---))
The reference to foo in (foo) at the right-hand-side expression of
bar is *also* done at expand time, the time in which the definition
of the foo macro clearly exists, expanded, evaluated, and ready to
be applied.
> (or else you must forbid separate compilation),
Clearly I must not forbid separate compilation since Ikarus provides
both this *and* separate compilation. Do you really think that
MzScheme's model is the only way to achieve separate compilation?
That would be a good joke.
> and if it's in a different world, pretending that it
> isn't by guessing which binding goes where can make the code a little
> shorter, but at the expense of hopelessly confusing some people.
The "think of the poor confused children" argument does not fly Eli.
> This thread started from a confusion over it, from someone who seems
> experienced enough to quickly get what's going on; how hard would a
> `define-syntax' newbie crash?
The only thing I saw Michele confused about was when he tried to use
mzscheme to be surprised that it did not allow him to do things that
were clearly meaningful without going through hoops (the last of which
would've lost him his day job).
> Any kind of beginner course that I have
> seen (which is much more than htdp-style courses) is making things
> explicit for newbies, because it helps to master things explicitly
> before you can deal with a "usually-fine" implicit guess.
Are you still making the "think of the poor noob" argument?
> -- So, "as if writing syntax-case macros was not hard
> enough"... exactly! -- Why complicate their lives with implicit
> guessing?
Stop saying "guessing". Nobody is guessing anything.
> As for R6RS modules being forbidden from having the same name exist at
> two levels if it doesn't come from the same source...
As a matter of fact, this point has never even been controversial
during the R6RS discussion period. If you thought that was not the
right behavior, why didn't you speak up? Or why didn't anyone else
think that it's better to be able to import different bindings with
different semantics but with the same name into different phases?
> Well... As someone once said "R6RS is \"perfect\".".
Wow. Didn't we go a long way just to go back to the "R6RS sucks"
argument.
Aziz,,,
It makes a case for why libraries should be expanded only once in
order "to avoid inconsistencies that may appear in the set of
identifiers exported by a library" which may result in unlinkable
programs.
It describes advantages of how a single-expansion guarantee can be
used productively.
It says: "the set of programs whose phases can be inferred is larger
than the set that can be explicitly specified. This is due to the fact
that the set of phases that the library can specify is fixed".
It also says: "Another reason why we chose to have no more than one
instance of a library is that system programming requires the ability
to manage resources, some of which cannot be replicated."
It also says: "It would be straightforward, however, to modify our
implementation to maintain separate environments for each phase and to
evaluate transformer and variable bindings once per phase. In
particular, the implicit phasing model does not inhibit doing so in
any way."
The R6RS says the (rnrs) library implicitly exports its bindings for
run and expand phases for convenience. However, importing (rnrs) for
other levels can still be necessary. The existence of negative levels
complicates things further. With the explicit phasing design, the
user has to figure out what these levels need to be, and for negative
levels this sounds even more confusing than for positive levels. The
implicit phasing design puts the burden of figuring out the necessary
phases on the implementation and not on the user.
There's also formal comments #92 and #123 which are about the implicit
versus explicit design [2][3]
[1] http://portal.acm.org/citation.cfm?id=1291151.1291197
[2] http://www.r6rs.org/formal-comments/comment-92.txt
[3] http://www.r6rs.org/formal-comments/comment-123.txt
--
: Derick
----------------------------------------------------------------
There is something I have always wanted to ask about Computer Science
papers. It seems that if I want to read the paper I must subscribe to
the site and to buy it. That looks strange to me, since I am used to
the publication system we use in Physics.
You must pay for peer-reviewed papers appeared in formal publications
(actually it is the University which pays the
subscription) however in practice nobody reads the published
versions, because he has already read the preprint.
Virtually all papers appears as preprint, freely downloadable
from the xxx archives (for instance from http://xxx.lanl.gov/form/hep-th
for Theoretical Physics). Preprints appear months before the official
publication, so people wanting to stay updated look at the preprints,
not at the formal publications. The formal publications are still
important, but only because they have much more value in your CV than
a preprint.
Is there something like that for Computer Science publications?
How commonly used is it?
Michele Simionato
Right -- note that the response to the first says:
| The current specification reflects a compromise among different
| possible models of libraries and syntax.
and the the response to the second:
| R6RS will maintain a compromise position that allows both explicit,
| enfored levels and implicit phasing (i.e., where `for' is
| effectively ignored).
In other words, the R6RS restriction on identifiers at different
levels is a compromise that helped avoid a controversy.
No, in fact the analogy is very relevant. Here's a more explicit
rephrase -- when I write:
> (define count!
(let ([c 0])
(lambda () (set! c (add1 c)) c)))
> (define-syntax foo
(lambda (stx)
(syntax-case stx ()
[(_ name) (with-syntax ([c (count!)])
#'(define (name) (list c (count!))))])))
I expect an error which is very much like a type error (where in this
case, the error is actually an unbound identifier). Therefore, the
result that Ikarus produces:
> (foo x)
> (x)
(1 2)
looks to me (note: to *me*) like a confusing result of the lack of
error. It looks to me like the same kind of "we'll do the right thing
instead of throwing an error" which results in 1+2=12 which you can
run into in languages that are heavy on the puns. Masking out these
kinds of errors is -- IMO -- an attempt to make the language *guess*
what you mean: "hey, we know how a number prints, we can just as well
append it as a string", and "hey, we have a binding at a different
level, we might as well use it".
> > However, I think that in the phase case the error is more severe:
Further explanation: it is more severe IMO because an "unbound
identifier" indicates a more important flaw in your code than "an
integer instead of a string".
> [...trivialities snipped...]
(See the last paragraph.)
> > You also said "As if writing syntax-case macros was not hard enough"
> > -- which is a good point: writing algorithms is a tough job,
> > adding weird puns like making numbers behave like string helps
> > write shorter code -- but it certainly does not help write code.
> > It's basically the language trying to second guess what you're
> > trying to say, in a way that is "mostly obvious enough" that you
> > won't get any nasty surprises -- and when done at the level of
> > bindings, it can be much more dangerous.
>
> Your Perl analogies aside again ...
>
> Who is "second guessing" what? And what's this nasty surprising
> danger that you speak of? Are you talking about nuclear missiles
> flying out of my expand-time environment? (because that would be
> funny)
I hope that the above clarifies these questions. Translating that to
an an amusing example involving erroneous call to
`send-nuclear-missiles' from a macro is left as an exercise.
> Sounds like you're trying to spread fear, uncertainty, and doubt
> about my system.
How did you get to *that* conclusion? Obviously, there's a design
decision to do here, and obviously there are reasons for both sides
expressed as two implementations. You started this whole thing with
pointing out these two sides:
| karus says: hey, we have the transformer, so, we might as well make
| it available. MzScheme says: hey, if you can't access run-time
| variables, then you can't access run-time macros either.
but later you chose to point to the Ikarus approach as better:
| As if writing syntax-case macros was not hard enough.
All I wanted to do was reiterate the fact that this coin has a flip
side too. I explicitly tried to avoid a "my scheme can piss farther
away than your scheme" contest, but you refused[*] to let it go at
that.
([*] By writing "I hope this suffices as an answer to Eli" when you're
aware of me not having any question in the first place. If you really
missed the fact that my "question" was a tongue-in-cheek paraphrase of
your own question then I apologize for assuming you did.)
The rest of your post is littered with snide remarks that will easily
lead to redundant flaming. (Some also appear above, and realizing
this, I chose to remove it together with the reply I wrote in an
attempt to stop the rolling ball of flames.) If you really want to
discuss this issue, then please focus on the technical aspects.
Personally, I have no interest in doing so, so the ball is in your
hands.
But why do you expect an unbound identifier error? Which identifier?
To me Ikarus is doing exactly what I would expect.
I am not interested in flames, I recognize that both you and Aziz are
much more knowledgeable than me. I can just report anedoctical
impressions from an user point of you. Still, sometimes it makes sense
to hear what users say.
I started programming in Scheme with PLT, but after a couple of months
I switched to Chicken, for various reasons. One of the reasons is that
I hated the phase separation thing. To me, it looked absurd that I had
to put helper functions for a macro in a different module, when they
were logically strictly connected to the macro and only to that macro.
For a while I thought Chicken did not have this phase separation and I
was happy with that. Then, I started compiled my scripts and
discovered that the phase separation was there, visible in compiled
code but not in interpreted code. So I was unhappy and I bugged Felix
for a while until I understood eval-when (this was nearly four years
ago). I sorta of understood that phase separation was needed in order
to ensure separate compilation so I decided it was a thing I had to
live with. Then, Ikarus came out, with
separate compilation and without phase separation. I still do not
understand how it works its magic, but it is a proof that the
semantics I have always wanted as an user is technically possible. At
this point I do not understand if phase separation is just a technical
convenience for compilation strategies such as Scheme->C translation
or if, as you imply, it is needed to avoid subtle bugs. If so, I would
like to see a better example than the one you give, since to me the
code is doing the right thing (there is a single binding for c, both a
compile time and runtime).
P.S. Eli's example written with my own sweet-macros library, just to
see how it looks:
(import (rnrs) (sweet-macros))
(define incr!
(let ([c 0])
(lambda () (set! c (add1 c)) c)))
(def-syntax (define-counter name)
#`(define (name) (list #,(incr!) (incr!))))
(define-counter c)
(c) ;=> (1 2)
The lack of the expected error is due to REPL.
Using the same two expressions in a top level program or library
results in:
$ ikarus --r6rs-script test.ss
Unhandled exception:
Condition components:
1. &who: count!
2. &message: "identifier out of context"
3. &syntax:
form: count!
subform: #f
4. &source-information:
file-name: "test.ss"
character: 187
5. &trace: #<syntax count! (char 187 of test.ss)>
Cheers
leppie
Indeed. Notice that in my answer to Eli I made a mistake talking about
helper functions when I should have said "helper macros".
The fact that helper functions have to be defined in a separate module
for compiled code did not confuse me, when writing sweet-macros. My
confusion arose from helper macros. This code contains an helper macro
"sub" and makes the issue clear:
(import (rnrs))
(define-syntax sub
(syntax-rules ()
((_ pattern template) (syntax-rules () (pattern template)))))
(define-syntax double
(sub (_ x) '(x x)))
(display (double x))
It works as I expect in Ikarus, both in compiled and interpreted code.
It does not work in PLT and I do not yet understand why. In my view
(please correct me if I am wrong) first of all the compiler look at
syntax definitions, then it expands the syntax forms and then it looks
at the resulting code for definitions. Clearly if the syntax form
needs an helper function to be defined, such a function has to be
available first, and that can be done by importing it from another
module and making it available at compile time. However in this case I
have a helper macro "sub", which is definite *at compile time* before
its use in double *at compile time*, so I expected that helper macro
to be recognized without putting it in an external module.
> The lack of the expected error is due to REPL.
>
> Using the same two expressions in a top level program or library
> results in: ...
That's correct. In Ikarus's repl, once a top-level variable is defined,
it is accessible from anywhere within the repl. But we were discussing
library semantics, so, the repl example from Eli was a red herring.
Aziz,,,
> Indeed. Notice that in my answer to Eli I made a mistake talking about
> helper functions when I should have said "helper macros".
> The fact that helper functions have to be defined in a separate module
> for compiled code did not confuse me, when writing sweet-macros.
Correct.
> My confusion arose from helper macros. This code contains an helper
> macro "sub" and makes the issue clear:
>
> (import (rnrs))
>
> (define-syntax sub
> (syntax-rules ()
> ((_ pattern template) (syntax-rules () (pattern template)))))
>
> (define-syntax double
> (sub (_ x) '(x x)))
>
> (display (double x))
>
> It works as I expect in Ikarus, both in compiled and interpreted code.
> It does not work in PLT and I do not yet understand why.
That's the question.
> In my view
> (please correct me if I am wrong) first of all the compiler look at
> syntax definitions, then it expands the syntax forms and then it looks
> at the resulting code for definitions.
It's a little more interleaved than that. In definitions context
(e.g., top-level program, library top-level, internal definitions),
it processes the body forms one by one looking for definitions. If
it encounters a define-syntax, the right-hand-side is expanded and
evaluated and made available for expanding subsequent forms from
that point on. If it encounters a macro use, it expands the macro,
splices it in place of the input form and resumes with the resulting
body forms. If it encounters a variable definition, it does NOT
expand the right-hand-side expression, instead, it queues it until
all definitions (variables and macros) are found. At that point,
the body expressions and right-hand-side expressions of variable
definitions (that were queued) are expanded.
At any rate, macro definitions *are* expanded and evaluated to
obtain the transformer procedure *before* expansion of the rest of
the definitions resumes. This is true for all R6RS implementations
regardless of what they think of cross-library phasing, or whether
macros are defined for single level or for all levels.
> Clearly if the syntax form
> needs an helper function to be defined, such a function has to be
> available first, and that can be done by importing it from another
> module and making it available at compile time.
Correct too. And in Ikarus, you don't need to do anything special
to make that happen.
> However in this case I
> have a helper macro "sub", which is definite *at compile time* before
> its use in double *at compile time*, so I expected that helper macro
> to be recognized without putting it in an external module.
The answer I gave to this was:
"Ikarus says: hey, we have the transformer, so, we might as well make
it available. MzScheme says: hey, if you can't access run-time
variables, then you can't access run-time macros either."
but this stirred some emotions, so, I guess it was not the right
answer, or might have not been phrased correctly, or whatever. I too
would like to know the right answer to your question.
Aziz,,,
[I should have been clearer about my example, but due to other reasons
preferred to avoid making my reply longer.]
I know that you get that error if you're not on the REPL -- but at a
minimum, I would expect the *same* error at the REPL. (Translation:
if I were using Ikarus, I would report the lack of error on the REPL
as a bug.) IMO, that would make the implicit phasing more consistent
and therefore more reasonable. Using the implicit number to string
coercion analogy: the fact that Perl/whatever is doing so can lead to
surprising bugs, but a good number of such bugs can be avoided if such
implicit coercion marks the binding (I should really talk about a
value) as a string. This means that `a=b+1' can still result in
appending "1" to `b', but if this happens then `b' will be treated
consistently as a string.
> Notice that in my answer to Eli I made a mistake talking about
> helper functions when I should have said "helper macros".
Your mistake is strongly related to the confusion I'm talking about.
A macro is different from a function in that it lives in a separate
world. In a language like C, macros are very obviously coming from a
different world because it's written in a different language. In a
lot of Scheme code, the language used for your macros is written the
same language, but it's still not the same world because of the way
they're evaluated: macros are called at compilation (aka expansion)
time sequentially, and other functions are called at runtime; when you
compile code the latter is never called, and when you run the result
the former is never called.
[In what follows I ignore multiple instantiations of modules. In PLT
this is a feature that allows me to use state in a reliable way in
macros, and I was told that the assumption in Ikarus is that
well-behaved macros should never use state. But to be able to talk
about this I'll need to know better what Ikarus does, and I'm too lazy
to be spending more weekend time...]
Now, Ikarus will guess (IIUC!) whether a particular piece of code goes
in the former or in the latter. By "guess" what I mean is that it
does so without an explicit indication in the user's code -- it does
that implicitly based on uses in the library. This relies on the
macro code being in the same language as runtime code -- and this
leads to the core of the difference. You (as an implementation
designer) might decide in advance that
The language used for writing macros is always the same language as
the one used for runtime code. [Possibly differs in the set of
bindings available, but any binding must have the same meaning,
which is the R6RS restriction.]
But in PLT (or more accurately, in macro systems that do (explicit)
phase separation), there is no need for the two languages to be the
same -- each one can draw its semantics from a different module. So
the question should be -- is this really a useful feature? Well, at
least in the PLT world it's very useful. The PLT code does have
several very different languages; among others there's a typed scheme
language, a lazy one, a reactive one, and even some
"superficially-different" ones like Swindle. There's good questions
there of what is the meaning of having your syntax-level code written
in some X language where X is not the "default" one -- does it make
sense to write macros in typed scheme? In lazy scheme? (That's a
confusing one.) There's other implications of conflating these
levels: PLT's two main executables are MzScheme and MrEd -- the latter
is a GUI extension of the former and requires a windowing system to
run in. So, conceptually speaking, should compiling gui code require
a windowing system too? (BTW, this is where the R6RS restriction can
help, together with something like a policy of not invoking modules
unless their bindings are used, but I won't get into that.)
I'm not writing all of these questions to highlight the PLT solution
as the obvious one or the Ikarus solution as a flawed one -- if you
begin with the assumption that macros are always in the same language,
then you will get a different set of answers than I do.
Sidenote: Back around 10 years ago I was playing with preprocessors,
roughly speaking, I'd write text and <<...>> would mark places with
Scheme that generates replacement text. In this case, the "macro"
language (scheme) is very different than the runtime language (text),
and a natural extension was to apply the same for Scheme code (giving
you text-level CPP-like macros that are written in Scheme), and soon
followed other games like using a different process for the meta-level
marks, possibly with a different language, having different langauges
specified at the meta-level and different processes used for
meta-meta-levels. Eventually, I was able to run text that looks like:
this is plain text
<<scheme code that generates text
<<scheme code that generates scheme code to generate text>>
more scheme code>>
<<TCL: tcl code to generate text, invoking a subprocess>>
<<scheme code <<TCL: tcl code to generate scheme code, running in
a separate subprocess>>>>
How is this related? It explains why *I* would not want the two
phases to be forced to use the same semantics. You're now free to
conclude that I'm clearly an insane freak and ignore any opinions I
express.
> The fact that helper functions have to be defined in a separate
> module for compiled code did not confuse me, when writing
> sweet-macros. My confusion arose from helper macros. This code
> contains an helper macro "sub" and makes the issue clear:
>
> (import (rnrs))
>
> (define-syntax sub
> (syntax-rules ()
> ((_ pattern template) (syntax-rules () (pattern template)))))
>
> (define-syntax double
> (sub (_ x) '(x x)))
>
> (display (double x))
>
> It works as I expect in Ikarus, both in compiled and interpreted
> code. It does not work in PLT and I do not yet understand why.
The right-hand-side of a `define-syntax' is at the syntax phase, and
syntax-phase functions are available to it. But if you want to write
a macro (double, in your case) that uses a macro (sub) in its body
(not in its result), then you're getting to the second (meta-meta)
level up. Here's an example that demonstrates conflating the meta and
meta-meta levels in Ikarus:
(define-syntax m1
(let ([c 0])
(lambda (stx)
(set! c (+ c 1))
(syntax-case stx ()
[(_) (with-syntax ([c c]) #'c)]))))
(define-syntax m2
(lambda (stx)
(syntax-case stx ()
[(_) (let ([c (m1)])
(with-syntax ([c c]) #'c))])))
(display (m1))
(display (m2))
> In my view (please correct me if I am wrong) first of all the
> compiler look at syntax definitions, then it expands the syntax
> forms and then it looks at the resulting code for
> definitions. Clearly if the syntax form needs an helper function to
> be defined, such a function has to be available first, and that can
> be done by importing it from another module and making it available
> at compile time. However in this case I have a helper macro "sub",
> which is definite *at compile time* before its use in double *at
> compile time*, so I expected that helper macro to be recognized
> without putting it in an external module.
Following PLT's phase separation, `sub' is a macro whose body is
written at the meta-meta level, so it doesn't necessarily use the same
semantics that `double' will use. In other words, `double' transforms
runtime code, and `sub' transforms macro code.
The 'error' will occur on the REPL as long as it is a single
expression. Hence the following results in error on REPL:
(let ()
(define count!
(let ([c 0])
(lambda () (set! c (add1 c)) c)))
(define-syntax foo
(lambda (stx)
(syntax-case stx ()
[(_ name) (with-syntax ([c (count!)])
#'(define (name) (list c (count!))))]))) )
Entering the 2 expressions separately is as good as separate
compilation, and thus no error.
Cheers
leppie
Well, the explicit phase model of PLT (and Larceny) were
invented precisely to prevent this kind of confusion. It is
generally a bad idea, when avoidable, for the same code to
behave differently when compiled than it would in a REPL.
Matthew Flatt wrote a good paper on this issue years ago.
The Dybvig-Ghuloum implicit phasing model has been argued to
be in some aspects more convenient. But it is also in some
ways less expressive. For example, in the Dybvig-Ghuloum
model it is impossible to define an r5rs-like library that
allows only SYNTAX-RULES to be used on the right hand side
of DEFINE-SYNTAX.
As the OP pointed out, users should be aware that superficially
R6RS libraries written for Ikarus do not necessarily port to
PLT/Larceny without modification. OTOH, superficially R6RS
PLT/Larceny libraries are more likely to port to Ikarus
without modification.
Andre
> The Dybvig-Ghuloum implicit phasing model has been argued to
> be in some aspects more convenient. But it is also in some
> ways less expressive. For example, in the Dybvig-Ghuloum
> model it is impossible to define an r5rs-like library that
> allows only SYNTAX-RULES to be used on the right hand side
> of DEFINE-SYNTAX.
"Impossible" is not the right word. This is trivial:
(library (r5rs-like)
(export define define-syntax syntax-rules quote lambda cons
cond + - ...)
(import (rename (rnrs) [define-syntax defstx]))
(defstx define-syntax
(syntax-rules (syntax-rules)
[(_ name (syntax-rules . rest))
(defstx name (syntax-rules . rest))])))
$ ikarus
> (import (r5rs-like))
> (define-syntax foo (lambda (x) 12))
Unhandled exception
Condition components:
1. &message: "invalid syntax"
2. &syntax:
form: (define-syntax foo (lambda (x) 12))
subform: #f
3. &trace: #<syntax (define-syntax foo (lambda (x) 12))>
Is this cheating?
Aziz,,,
Right on. Definitions entered into the repl *are* indeed expanded and
evaluated separately and sequentially as they're entered. There is no
technical reason why they cannot be made available at expand-time code
just as well as run-time code. Adding the restriction to Ikarus to
disallow such references is a trivial exercise in enforcing arbitrary
restrictions, which I won't do of course, just like how I won't force
an error when a syntax definition, an expand-time binding, is used at
expand time.
Aziz,,,
> Well, the explicit phase model of PLT (and Larceny) were
> invented precisely to prevent this kind of confusion. It is
> generally a bad idea, when avoidable, for the same code to
> behave differently when compiled than it would in a REPL.
> Matthew Flatt wrote a good paper on this issue years ago.
Matthew's paper was more of a critique of how programs without
modules used to be compiled and the errors that ones gets into
when trying to compile a program that runs fine in the repl.
I agree with all of that. Indeed, the only things that can be
compiled in Ikarus are libraries, which have better properties
when it comes to separate compilations. Ikarus neither does,
nor ever will attempt to compile repl interactions because that
makes no sense. The repl in my view is a cute little calculator
in which you can type your little factorial program and run it.
It was never meant to have separate-compilation semantics, so
why force library semantics to it? Other things in which the
repl differs from libraries are (1) intermixing definitions and
expressions, (2) allowing multiple definitions/redefinitions of
the same variable, (3) continuation semantics, among others.
Does Larceny/PLT provide exactly 1-to-1 correspondence between
their library semantics and repl semantics? I doubt it.
Aziz,,,
> There is something I have always wanted to ask about Computer Science
> papers. It seems that if I want to read the paper I must subscribe to
> the site and to buy it. That looks strange to me, since I am used to
> the publication system we use in Physics.
> You must pay for peer-reviewed papers appeared in formal publications
> (actually it is the University which pays the
> subscription) however in practice nobody reads the published
> versions, because he has already read the preprint.
> Virtually all papers appears as preprint, freely downloadable
> from the xxx archives (for instance fromhttp://xxx.lanl.gov/form/hep-th
> for Theoretical Physics). Preprints appear months before the official
> publication, so people wanting to stay updated look at the preprints,
> not at the formal publications. The formal publications are still
> important, but only because they have much more value in your CV than
> a preprint.
> Is there something like that for Computer Science publications?
> How commonly used is it?
The arxiv itself has a section for computer science at
http://arxiv.org/corr/home, but it does not seem to have
been used as extensively.
As a fellow Physicist, I am continually dumbfounded and
frustrated by the inability of CS people to organize a
useful centralized preprint repository like the one we
Physicists have had for decades now (or even just to use
the one we made for them, or as a last resort just to post
preprints of their own papers on their homepages where
Google can find them).
I mean, for heavens sake, guys, you are computer scientists!
Get with the program already and learn to use the internet!
Andre
Yes, because it does not generalize.
Andre
> It's a little more interleaved than that. In definitions context
> (e.g., top-level program, library top-level, internal definitions),
> it processes the body forms one by one looking for definitions. If
> it encounters a define-syntax, the right-hand-side is expanded and
> evaluated and made available for expanding subsequent forms from
> that point on. If it encounters a macro use, it expands the macro,
> splices it in place of the input form and resumes with the resulting
> body forms. If it encounters a variable definition, it does NOT
> expand the right-hand-side expression, instead, it queues it until
> all definitions (variables and macros) are found. At that point,
> the body expressions and right-hand-side expressions of variable
> definitions (that were queued) are expanded.
Do you plan to put some version of whatever model
is described in your paper online somewhere where regular people
can get at it without paying? It seems rather pointless to
continue this discussion otherwise, since we don't know what
we are comparing against.
Andre
What I mentioned above is not related to the paper. It is pretty
much what R6RS's expansion process says in:
http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-13.html#node_chap_10
As for the paper, I will try to find out how to obtain permission
to redistribute it, give