yinso.c@gmail.com skrev:
> Hi Jens,
> Thanks for your comment - you are always a great help - please see
> inline.
> On Apr 27, 1:26 am, Jens Axel Sgaard <use@soegaard.net> wrote:
>> The expression
>> (datum->syntax-object #'stx list-of-symbols)
>> returns a syntax-object representing a list of symbols.
>> And this is the problem.
>> What is needed is a list of identifiers, each with
>> the same context as the macro call (that is stx).
>> This means that you need to convert each symbol in the
>> list into a syntax-object (and not the entire list).
>> That is:
>> (map (lambda (s) (datum->syntax-object stx s)
>> (syntax-object->datum #'(param ...)))
> This is cool technique, thanks.
>> If you want to process the identifiers, then you need
>> to use the list of identifiers instead of the list
>> of symbols.
> What you are saying is symbol reintroduced with syntax context !=
> identifier.
No. I am saying that (datum->syntax-object stx list-of-symbols)
is a syntax object representing a list of symbols - and not a
syntax-object representing a list of identifiers.
(datum->syntax-object stx
(list (datum->syntax-object stx 'a)
(datum->syntax-object stx 'b))
is on the other hand a syntax object representing a
list of identifiers.
> I did a bit more hacking and found that if I use syntax-e
> I can just convert the top element & leave the inner part's syntax
> intact and it will work (per your point the inner parts still remain
> identifiers).
If you have a list, you can use syntax->list.
> What I found puzzling though, is that #'_ will give me back the "close-
> to-original" context (explain in a bit), but #'stx , #'(param ...)
> (the part where that should have kept the original context) or #(exp
> exp2 ...) don't
> - that tells me the different syntax held different
> context for blessing, and it's confusing which one to use if I am
> mixing up like above. Are there specific things that I should know
> not to step on here?
> *** the "close-to-original" part ***
> I found that while my original solution works for the lambda1, it's
> not composable. I cannot create a define1 that generates a lambda1
> and make it work.
> (define-syntax define1
> (syntax-rules ()
> ((_ (name arg ...) exp exp2 ...)
> (define name (lambda1 (arg ...) exp exp2 ...))))
> In such case it would fail with the same error (even when lambda1
> works) - reference to undefined identifier. So I guess the #'_
> doesn't provide the right context after all ;)
Ah! My "solution" weren't as good as I hoped for. Actually
it is broken.
What happens with define1 ?
Let's look at
(define1 (add2 x y) (+ x y)) .
Let's give this definition the syntax context number 1:
(define1:1 (add2:1 x:1 y:1) (+:1 x:1 y:1)) .
The transformer of define1 expands this into:
(define:2 add2:1 (lambda1:2 (x:1 y:1) (+:1 x:1 y:1))
Note that lambda1:2 indicates that lambda1 was introduced
by this call of the define1-transformer.
Now the transformer of lambda1 is called. It expands to
(define:2 add2:1 (lambda:3 (x:2 y:2) (+:1 x:1 y:1))
Why is the arguments given context 2? That's due to
my "solution":
(map (lambda (s) (datum->syntax-object stx s)
(syntax-object->datum #'(param ...)))
where the context of the macro call held by stx
is copied.
In our original example (lambda1 (a b) (+ a b))
this wasn't a problem, since the context of the
macro call and lambda1 were the same.
In the define1-example the contexts are different,
and we run into problems.
How can we fix this? Well, we need args to have
the same context as the the expressions in body of
lambda1, so instead of copying the context of stx,
we should copy the context of, say, the first expression.
Or should we? If the first expression were introduced
by a macro and the second from user code, then the
two expressions have different context - and we run
into the same problem.
Can we copy the context from the first parameter?
Nope - the same argument applies - if one of the
parameters originates from a macro expansion and
the other doesn't we once again run into problems.
What then? Well, we need to copy the context
of the first parameter to the first "argument" in
the expansion. And the context of second to ...
(map (lambda (orig new) (datum->syntax-object orig new))
(syntax->list #'(param ...))
(syntax-object->datum #'(param ...)))
In this example you use identity - so we could have
written (syntax->list #'(param ...)) with the same
result.
> (*** wishful thinking, probably, from here on out ***)
> The reason I stripped away all syntax info is so I can reuse functions
> and not have to specifically process syntax objects. For example, map
> can only work with list and not syntax list, so syntax-e is required -
Use syntax->list to convert a syntax object representing a list in to
a list.
> all together it just makes syntax-case very verbose-happy (when one
> needs to break hygiene). What I am saying is that it would be nice to
> treat the processing part just like other symbols and not have to
> worry about whether it's syntax or not, so it can be fewer lines of
> code.
> So
> (syntax-case stx ()
> (with-syntax ((args (map identity #'(param ...))))
> #'(lambda args exp exp2 ...))))
> is preferrable to
> (syntax-case stx ()
> (with-syntax ((args (datum->syntax-object #'stx (map identity
> (syntax-object->datum #'(param ...))))))
> #'(lambda args exp exp2 ...)))))
> I wrote a stx-map & stx-proc function just to abstract over the
> conversion code, but not sure if that's the best style.
The syntax collection has quite a few of these:
<http://svn.plt-scheme.org/plt/trunk/collects/syntax/doc.txt>
Btwe - R6RS represents list syntax with a list (and not
as a syntax-object as PLT Scheme does). So in due time,
you'll have your wishes fulfilled.
> Are there guidelines or collective sets of wisdoms that will enable
> writing less verbose syntax-case code (style guide, etc)?
Flatt always gives *very* good advice on macro related topics
on the PLT list. Check the archive.
--
Jens Axel Sgaard