Home     |     .Net Programming    |     cSharp Home    |     Sql Server Home    |     Javascript / Client Side Development     |     Ajax Programming

Ruby on Rails Development     |     Perl Programming     |     C Programming Language     |     C++ Programming     |     IT Jobs

Python Programming Language     |     Laptop Suggestions?    |     TCL Scripting     |     Fortran Programming     |     Scheme Programming Language


 
 
Cervo Technologies
The Right Source to Outsource

MS Dynamics CRM 3.0

Scheme Programming Language

help with syntax contexts - confused


Hi all -

I am getting confused about the syntax context w.r.t combining with
datum->syntax-object - I am unclear about the rule about which context
is necessary to bless the datum.  Consider the following simplified/
contrived example  in PLT Scheme.  I think this is not implementation
specific, but please let me know if I am wrong.

; lamda1 == lambda
(require-for-syntax (lib "etc.ss"))
(define-syntax lambda1
  (lambda (stx)
    (syntax-case stx ()
      ((_ (param ...) exp ...)
       (with-syntax ((args (datum->syntax-object
                            #'stx ; => fails to bless
                            (map
                             identity ; *** replace identity with
other func for arg processing
                             (syntax-object->datum #'(param ...))))))
         #'(lambda args exp ...))))))

(define add (lambda1 (a b) (+ a b)))
(add 2 3) ; reference to undefined identifier: a

lambda1 strips the arguments of its syntax context and try to bless it
back via #'stx, but it doesn't work.  (+ a b) expands to (#%app (#
%top . +) (#%top . a) (#%top . b))

This bugs me to no end until I accidentally replace #'stx with #'_,
and it magically worked.

; lambda1 == lambda
(require-for-syntax (lib "etc.ss"))
(define-syntax lambda1
  (lambda (stx)
    (syntax-case stx ()
      ((_ (param ...) exp ...)
       (with-syntax ((args (datum->syntax-object
                            #'_ ; => works - how???
                            (map
                             identity
                             (syntax-object->datum #'(param ...))))))
         #'(lambda args exp ...))))))

(define add (lambda1 (a b) (+ a b)))
(add 2 3) ; => 5

And the other two candidates, #'(param ...) or #'(exp ...) don't work
either.

So the question is - why?  And what is the guideline to do this
without resorting to trial & error?   Any pointers are appreciated.

Thanks!
yinso

yinso.c@gmail.com skrev:

Here is what happens, when you expand the above using the
Macro Stepper in PLT Scheme:

(define add (lambda1 (a b) (+ a b)))
   -> Macro transformation
(define-values:18 (add) (lambda1 (a b) (+ a b)))
   ->  Macro transformation ***
(define-values:18 (add) (lambda:19 (a:19 b:19) (+ a b)))

The macro transformation marked with *** is you performed
by your macro transformer (since lambda1 disappears).

Notice that all identifiers introduced (that is not present
in the original syntax) by lambda1 is marked with :19.
Note also that the parameters suddenly are marked with :19
where as the a and b in the expression are umarked.
This is the problem - but why did it happen?

The expression
   (syntax-object->datum #'(param ...))
returns a list of symbols.

The expression
   (map identify (syntax-object->datum #'(param ...)))
returns a list with the same symbols.

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 ...)))

If you want to process the identifiers, then you need
to use the list of identifiers instead of the list
of symbols.

--
Jens Axel Sgaard

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.  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).

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 ;)

(*** 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 -
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.

Are there guidelines or collective sets of wisdoms that will enable
writing less verbose syntax-case code (style guide, etc)?

Thanks.
yinso

yinso.c@gmail.com skrev:

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.

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.

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

On Apr 27, 4:17 am, Jens Axel Sgaard <use@soegaard.net> wrote:

> 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.

Aha!  That make sense!  Thanks ;)

I think the bookkeeping of which syntax object to use is what tripped
me up all over the place on this one, thanks for the explanation,
seems like - bless the datum back with its original context is the
right thing to do, until I get more familiar with out to do macro
expansion bookkeeping ;)

> 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.

> Flatt always gives *very* good advice on macro related topics
> on the PLT list. Check the archive.

Thanks for the pointers and links!!  Very appreciated ;)

yinso

Add to del.icio.us | Digg this | Stumble it | Powered by Megasolutions Inc