For Programmers: Free Programming Magazines  


Home > Archive > Scheme > April 2006 > help with macros - two challenges









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author help with macros - two challenges
xscottg@gmail.com

2006-04-18, 4:04 am

I'm new to Scheme and trying to wrap my head around define-syntax and
macros. Any suggestions on how to implement the following?

First, an "iff" macro with some extra syntax to increase readability.
This doesn't compile with DrScheme, but it's the closest I've gotten:

(define-syntax iff
(syntax-rules (elif: else:)
((_ expr tcode ...)
(if expr (begin tcode ...)))
((_ expr tcode ... else: fcode ...)
(if expr (begin tcode ...) (let () fcode ...)))
((_ expr tcode ... elif: rest ...)
(if expr (begin tcode ...) (iff rest ...)))
))

I'd like to be able to write things like:

(iff (some condition)
(here is one of multiple statements)
(here is one of multiple statements)
(here is one of multiple statements)
elif: (another condition)
(more statements)
(more statements)
else:
(more statements)
(more statements)
)


If I add some more parens to the patterns, I can make it work, but they
shouldn't be needed as "elif:" and "else:" are adequate delimiters.
BTW, I realize Schemers would typically do this with "cond". However,
I'd still like to know how to write the macro.


Second, I'd like to know how to write a macro that creates a macro.
For example imagine a macro called "proc" that creates a new macro that
places all of it's arguments in a lambda for delayed evaluation.

; Use the meta-macro "proc" to create a simple macro "foo"
(proc (foo a b c)
(various statements)
(where (a) through (c) can be evaluated)
(maybe multiple times (a) (a) (c))
; or not at all, in the case of b
)

; Then this expression:
(foo (print "first argument") (print "second") (print "third"))
; Expands to
; (let (
; (a (lambda () (print "first argument"))
; (b (lambda () (print "second"))
; (c (lambda () (print "third"))
; )
; (various statements)
; (where (a) through (c) can be evaluated or not)
; (maybe multiple times (a) (a) (c))
; )

The question is how to define "proc" in the example above. If you're
really up for the challenge, I'd like to know how to handle optional
arguments:

(proc (foo . rest) (bleah bleah) ((car rest)) (bleah))


I haven't made much headway in understanding syntax-case macros yet, so
solutions using syntax-rules are much better for me. Either that, or
maybe give me a good pointer to a web page describing syntax-case too.
:-)

Thanks in advance for all replies!

Cheers,
-Scott

pschombe@uci.edu

2006-04-18, 4:04 am

I did something similiar about a month ago, although my version was
only if / else, but I am sure that you could extend it. See here:
https://pschombe.wordpress.com/2006...n-if-extension/

xscottg@gmail.com

2006-04-18, 7:06 pm

Thanks for the link. I'll poke through that tonight. Any thoughts on
how to implement "proc"?

Cheers,
-Scott

pschombe@uci.edu

2006-04-18, 7:06 pm

I don't undestand why proc has to generate a macro. Simply write a
macro foo that has a syntax like a let statment:

(foo
((a expr1)
(b expr2)
(c expr3))
body
...)

Thus the macro could be written:
(define-syntax foo
(lambda (x)
(letrec (
(lambdize
(lambda (lst)
(if (null? lst)
'()
(cons (cons (car (car lst)) `((lambda () ,@(cdr
(car lst))))) (lambdize (cdr lst))))))
(mainparser
(lambda (x)
`(let ,(lambdize (car x)) ,@(cdr x))))
)
(datum->syntax-object (syntax x) (mainparser (cdr
(syntax-object->datum x))))
)))

and could be used like:
(foo ((a (print "first argument")) (b (print "second")) (c (print
"third")))
(a) (a) (b) (c))

xscottg@gmail.com

2006-04-18, 7:07 pm

> I don't undestand why proc has to generate a macro.

Is there any other way to create a procedure or function that doesn't
evaluate it's arguments?

Mostly, I'm trying to learn how to use the macro facility. I gave
myself a small list of challenges that I believe should be possible,
and I'm hoping to learn from them even if there is a more conventional
approach. If it's not possible for some reason, I'd like to know that
too. It looks like you can have a define-syntax inside of another
define-syntax, so a macro can be used to declare another macro, but I
haven't figured out how to get all the parameters correct.

Also, Scheme seems to be more of a language where you decide how you'd
like to express something, and then extend the language to make that
expression possible. If I simply wrote a macro for "foo" in my example
above, I'd have to write macros for "baz", "bar", "bum", and every
other thing I think of as a "proc" (procedure with
unevaluated/lambda-ized arguments). That would be longer and uglier
code than should be necessary.


> Simply write a macro foo that has a syntax like a let statement


This misses the point. The resultant "foo" would be called in multiple
places. It's basically a callable thing that chooses when to evaluate
its arguments. If I had to retype the body of "foo" at every place it
is used, I might as well explicitly pass the arguments in as lambdas.


Anyway, your previous response gave me some ideas. It looks like
instead of trying to do everything inside of a single macro, I need to
break it into multiple macros to handle the multiple levels of ...
patterns.

Cheers,
-Scott

pschombe@uci.edu

2006-04-18, 10:02 pm

There is a difference between generating a macro within a macro and
simply writing a macro.

Foo is a macro, it will only be called at compile time, it is not a
function. It does not evaluate its arguments, try the definition I
provided and see for youself.

xscottg@gmail.com

2006-04-18, 10:03 pm

> There is a difference between generating a macro within
> a macro and simply writing a macro.
>
> Foo is a macro, it will only be called at compile time, it is
> not a function. It does not evaluate its arguments, try the
> definition I provided and see for youself.


I understand what a macro is and when it's invoked. I don't understand
all the ins and outs of defining macros, and that's why I posted this
question. I don't think you understand what it is I'm trying to do,
and that's possibly my fault for not being clear enough. However,
trust me that your example does not answer what I'm asking.

Here's another way of putting the question. Let's say I want to create
a macro "foo" that takes 3 arguments, but wraps those arguments in a
lambda, I *could* type:

(define-syntax foo
(syntax-rules ()
((_ expr-a expr-b expr-c)
(let (
(a (lambda () expr-a))
(b (lambda () expr-b))
(c (lambda () expr-c))
)
; body of code that can evaluate a b and c zero or more times
))))

This compiles and has pretty much the behavior I want for "foo" [1].
However, that's a lot of boiler plate for something I might do in many
places. I don't want to type that every time I need to create
something like foo. What I'd really rather type is:

(define-with-lambda-ized-arguments (foo a b c)
; body of code that can evaluate a b and c zero or more times
)

Then I can use "define-with-lambda-ized-arguments" in multiple places
and not duplicate all the boiler plate. The
"define-with-lambda-ized-arguments" is what I was calling "proc" above.
So my question is how to declare "define-with-lambda-ized-arguments"?


Cheers,
-Scott


[1] This doesn't have exactly the behavior I want since the body will
be expanded everywhere "foo" is called and the lexical scoping would be
wrong. I'd rather there just be a single function body for "foo" in
the correct lexical scope, and have the macro only lambda-ize the
arguments everywhere "foo" is called. Something more like:

(define (lexically-correct-foo-body a b c)
; body of code that can evaluate a b and c zero or more times
)

(define-syntax foo
(syntax-rules ()
((_ a b c) (lexically-correct-foo-body (lambda () a) (lambda ()
b) (lambda () c))))

But then the macro "define-with-lambda-ized-arguments" needs to expand
to both a function declaration and a macro declaration, and that
doesn't seem to be allowed...

Anton van Straaten

2006-04-19, 4:04 am

xscottg@gmail.com wrote:
> So my question is how to declare "define-with-lambda-ized-arguments"?


Easy:

(define-syntax define-with-lambda-ized-arguments
(syntax-rules ()
((_ (f a b c) body ...)
(define-syntax f
(syntax-rules ()
((f p q r)
(let ((a (lambda () p))
(b (lambda () q))
(c (lambda () r)))
body ...)))))))

If you want to generalize this to any number of arguments, one thing
you're going to need to know is that ellipses introduced in meta-macros
need to be specified as "(... ...)", and ditto for uses of those ellipses.

> I'd rather there just be a single function body for "foo" in
> the correct lexical scope, and have the macro only lambda-ize the
> arguments everywhere "foo" is called. Something more like:
>
> (define (lexically-correct-foo-body a b c)
> ; body of code that can evaluate a b and c zero or more times
> )
>
> (define-syntax foo
> (syntax-rules ()
> ((_ a b c) (lexically-correct-foo-body (lambda () a) (lambda ()
> b) (lambda () c))))
>
> But then the macro "define-with-lambda-ized-arguments" needs to expand
> to both a function declaration and a macro declaration, and that
> doesn't seem to be allowed...


You can do this using the 'begin' form, which is designed to support this:

(define-syntax define-with-lambda-ized-arguments
(syntax-rules ()
((_ (f a b c) body ...)
(begin
(define (f-internal a b c) <stuff> )
(define-syntax f
<stuff>

This version will be easier to generalize to N arguments, because you
won't need to deal with subexpressions like (a (lambda () p)), which
interleave macro arguments from both the outer and inner macros,
requiring some creativity to deal with.

Anton
pschombe@uci.edu

2006-04-19, 4:04 am

but in the end, to work it will have to expand to the same thing.
Creating a proc macro adds to your work, not takes it away, because
instead of caputring the variables on the spot with names that are
meaningful in the situation you have to pre-make the macro with the
names you want and then invoke it.

Here is the iff macro you wanted by the way:

(define-syntax iff
(lambda (x)
(letrec
(
(clause-transformer
(lambda (condition poscase remaining)
(cond
((null? remaining) (if (null? poscase) '() `(if
,condition (begin ,@poscase))))
((eq? (car remaining) 'else) (if (null? poscase)
`(if (not ,condition)
(begin ,@(cdr remaining)))
`(if ,condition (begin
,@poscase) (begin ,@(cdr remaining))) ))
((eq? (car remaining) 'elseif) (if (null? poscase)
`(if (not ,condition)

,(clause-transformer (car (cdr remaining)) '() (cdr (cdr remaining))))
`(if ,condition
(begin ,@poscase)

,(clause-transformer (car (cdr remaining)) '() (cdr (cdr remaining))))
))
(else (clause-transformer condition (append poscase
(list (car remaining))) (cdr remaining))))))

(main-transformer
(lambda (lst)
(clause-transformer (car lst) '() (cdr lst))))
)
(datum->syntax-object x (main-transformer (cdr
(syntax-object->datum x)))))))

You can use it just like you would expect,
ex:
(define test (lambda (z)
(iff (< z -5)
(display "less than -5")
elseif (<= z 5)
(display "between -5 and 5, inclusive")
else
(display "greater than 5"))))

xscottg@gmail.com

2006-04-19, 7:06 pm

> ellipses introduced in meta-macros need to be specified as "(... ...)"

Very . I think this is the piece I was missing. Thank you!

> You can do this using the 'begin' form, which is designed to support this:


I didn't have a lot of time to experiment with this last night, but it
looked like (in DrScheme) the function definition and macro definition
only have scope inside of the begin block. Although a quick check with
Guile here at work shows this not to be the case.... I'll have to play
some more tonight. Thanks again!

Cheers,
-Scott

xscottg@gmail.com

2006-04-19, 7:06 pm

> but in the end, to work it will have to expand to the same thing.

Fair enough, but it's the syntactic sugar that makes it pleasantly
readable.

> Creating a proc macro adds to your work, not takes it away, because
> instead of caputring the variables on the spot with names that are
> meaningful in the situation you have to pre-make the macro with the
> names you want and then invoke it.


We're not seeing eye to eye on this one. I'm trying to create
something that acts in all ways like a procedure except that it doesn't
pre-evaluate the arguments. What you're suggesting looks more like
copying and pasting a let expression everywhere I want to invoke the
procedure. The variable names are more meaningful in the declaration
of the procedure, not so much at the place where the procedure is
invoked.

> Here is the iff macro you wanted by the way:


Thank you! I've been wondering what sort of magic was being done by
syntax-rules and syntax-case. It looks like your example will help me
understand what's really going on and how to create more elaborate
macros.

Cheers,
-Scott

ggem

2006-04-19, 7:06 pm

pschombe@uci.edu wrote:

> Here is the iff macro you wanted by the way:
> [ ... long macro definition deleted ...]


I'd suggest you don't play with datum->syntax-object and
syntax-object->datum while you are learning. And after that,
use it only if you want to break hygiene if possible.

Here is a definition that doesn't break hygiene:

(define-syntax iff
(syntax-rules ()
((iff expr ...)
(%iff-help (expr ...) ()))))

(define-syntax %iff-help
(syntax-rules (elif: else:)
((%iff-help () (test expr ...))
(if test (begin expr ...)))
((%iff-help (else: expr-e ...) (test expr-t ...))
(if test (begin expr-t ...) (begin expr-e ...)))
((%iff-help (elif: expr-e ...) (test expr-t ...))
(if test (begin expr-t ...) (%iff-help (expr-e ...) ())))
((%iff-help (expr0 expr ...) (expr-t ...))
(%iff-help (expr ...) (expr-t ... expr0)))))

-ggem.

ggem

2006-04-19, 7:06 pm

pschombe@uci.edu wrote:

> Here is the iff macro you wanted by the way:
> [ ... long macro definition deleted ...]


I'd suggest you don't play with datum->syntax-object and
syntax-object->datum while you are learning. And after that,
use it only if you want to break hygiene if possible.

Here is a definition that doesn't break hygiene:

(define-syntax iff
(syntax-rules ()
((iff expr ...)
(%iff-help (expr ...) ()))))

(define-syntax %iff-help
(syntax-rules (elif: else:)
((%iff-help () (test expr ...))
(if test (begin expr ...)))
((%iff-help (else: expr-e ...) (test expr-t ...))
(if test (begin expr-t ...) (begin expr-e ...)))
((%iff-help (elif: expr-e ...) (test expr-t ...))
(if test (begin expr-t ...) (%iff-help (expr-e ...) ())))
((%iff-help (expr0 expr ...) (expr-t ...))
(%iff-help (expr ...) (expr-t ... expr0)))))

-ggem.

Anton van Straaten

2006-04-19, 7:06 pm

xscottg@gmail.com wrote:
>
>
> I didn't have a lot of time to experiment with this last night, but it
> looked like (in DrScheme) the function definition and macro definition
> only have scope inside of the begin block. Although a quick check with
> Guile here at work shows this not to be the case....


Unfortunately, Guile is broken in this respect - it violates hygiene in
a way that syntax-rules is not supposed to allow. It can't be used as
an indicator of expected Scheme behavior. (I've appended two examples
that demonstrate Guile bugs in this area.)

The major Scheme implementations, including DrScheme, all do what R5RS
says. R5RS defines two rules that are relevant here.

First, definitions inside a BEGIN form are equivalent to those same
definitions without the BEGIN. This feature was specifically designed
to support use in macros like the one we're discussing.

Second, syntax-rules macros are supposed to be hygienic, which among
other things means that they can't introduce new names into the
environment that invokes them. If you want to use a syntax-rules macro
to define a name, you have to specify that name as part of the input to
the macro.

In an R5RS-compliant implementation, the consequences for your macro are
straightforward: you want to define a macro that's visible to the
invoking environment, whereas (I assume) you don't want the function to
be visible, and that's easy to do. The (almost complete) skeleton I
gave in my previous message did this:

(define-syntax define-with-lambda-ized-arguments
(syntax-rules ()
((_ (f a b c) body ...)
(begin
(define (f-internal a b c) body ...)
(define-syntax f
<stuff>
(f-internal a b c)
<more stuff>

The inner macro will be bound to an identifier from the calling
environment, i.e. the identifier specified by the caller of the macro
and bound to the pattern variable "f". The caller of the outer macro
will therefore be able to invoke the newly defined macro using the
specified name.

However, the definition of the helper function, f-internal, uses a name
that wasn't introduced as a pattern variable. The hygiene rules
therefore require that "the identifier will in effect be renamed
throughout its scope to avoid conflicts with other identifiers" (R5RS
4.3). This means that the invoking environment will be unable to
directly access f-internal.

If you wanted to expose the f-internal procedure to the invoking
environment, you'd need to add another argument (pattern variable) to
your macro so that the caller could specify a name for it.

Anton


* * *
Guile bug demo:

(define-syntax test-hygiene
(syntax-rules ()
((_ f)
(begin
(define hidden 42)
(define (f) hidden)))))

(test-hygiene foo) ; defines macro foo
(foo) ;=> 42
hidden ;=> 42 (wrong; demonstrates a Guile hygiene error)

; now let's wrap the above in a procedure
(define (bar)
(define-syntax test-hygiene
(syntax-rules ()
((_ f)
(begin
(define hidden 42)
(define (f) hidden)))))
(test-hygiene foo)
(foo))

This should work fine in an R5RS-compliant Scheme, but Guile responds with:

ERROR: invalid context for definition of hidden
ABORT: (misc-error)
Abdulaziz Ghuloum

2006-04-19, 7:06 pm

Anton van Straaten wrote:

> The major Scheme implementations, including DrScheme, all do what R5RS
> says. R5RS defines two rules that are relevant here.
>
> First, definitions inside a BEGIN form are equivalent to those same
> definitions without the BEGIN. This feature was specifically designed
> to support use in macros like the one we're discussing.
>
> Second, syntax-rules macros are supposed to be hygienic, which among
> other things means that they can't introduce new names into the
> environment that invokes them. If you want to use a syntax-rules macro
> to define a name, you have to specify that name as part of the input to
> the macro.
> [...]


> Guile bug demo:
>
> (define-syntax test-hygiene
> (syntax-rules ()
> ((_ f)
> (begin
> (define hidden 42)
> (define (f) hidden)))))
>
> (test-hygiene foo) ; defines macro foo
> (foo) ;=> 42
> hidden ;=> 42 (wrong; demonstrates a Guile hygiene error)


I know that at least MzScheme and Chez Scheme introduce hidden as a
fresh global variable that only the macro-introduced code can access.
I'm not sure though that this is required for top-level variables.

I don't have access to guile at the moment or I would've tested it like:

(define-syntax test-hygiene
(syntax-rules ()
((_ f)
(begin
(define hidden 42)
(define (f) hidden)))))

(let ()
(test-hygiene foo)
(list (foo) hidden))

to see if hidden was introduced unhygienically.

> ; now let's wrap the above in a procedure
> (define (bar)
> (define-syntax test-hygiene
> (syntax-rules ()
> ((_ f)
> (begin
> (define hidden 42)
> (define (f) hidden)))))
> (test-hygiene foo)
> (foo))
>
> This should work fine in an R5RS-compliant Scheme, but Guile responds with:
>
> ERROR: invalid context for definition of hidden
> ABORT: (misc-error)


Internal define-syntax is not required by r5rs.

Section 5.3 (Syntax definitions) states:
``Syntax definitions are valid only at the top level of a <program>.''
Also,
`` There is no define-syntax analogue of internal definitions.''

Aziz,,,
pschombe@uci.edu

2006-04-19, 7:06 pm

> Internal define-syntax is not required by r5rs.

More importantly you shouldn't need it, at least I have never come
across a problem that couldn't be solved without them, so if you are
just starting off with macros why bother worrying about something you
will probably never use?

xscottg@gmail.com

2006-04-19, 7:06 pm

> I'd suggest you don't play with datum->syntax-object
> and syntax-object->datum while you are learning.


How else would I learn about datum->syntax-object and
syntax-object->datum then?

Just kidding. :-) I appreciate where you're coming from and your
hygienic example. But I haven't had much luck finding a good tutorial
on the innards of macros to explain this stuff, and the non-hygienic
examples help bring home what's going on under the hood.

As a Scheme newcomer (with subtantial experience in other programming
languages), I can look at the lambda expression and see what's going on
in terms of manipulating the parse tree. Just seeing syntax-rules or
syntax-case makes the whole thing look like magic.

Cheers,
-Scott

Jens Axel Søgaard

2006-04-19, 7:06 pm

xscottg@gmail.com wrote:

> As a Scheme newcomer (with subtantial experience in other programming
> languages), I can look at the lambda expression and see what's going on
> in terms of manipulating the parse tree. Just seeing syntax-rules or
> syntax-case makes the whole thing look like magic.


Al* Petrofsky wrote a very nice piece in this group:

<http://groups.google.com/group/comp...b6cc6e11775b619>

You are by no means the first to ask the question. The references
in this thread

<http://groups.google.com/group/comp...6c338837de3a020>

should give food for thought.

If you want more detail with respect to the *implementation* if
syntax-case use the source^H^H^H^H^H^Hpaper.

<http://www.cs.indiana.edu/~dyb/pubs...4-pp295-326.pdf>

Whether or not you are interested in the implementation of syntax-case,
the paper "Writing hygienic macros in Scheme with syntax-case" is
well worth the time spent reading it:

<http://www.cs.indiana.edu/~dyb/pubs/tr356.pdf>

--
Jens Axel Søgaard
<http://www.scheme.dk/blog/>
Anton van Straaten

2006-04-19, 7:06 pm

Abdulaziz Ghuloum wrote:
> I know that at least MzScheme and Chez Scheme introduce hidden as a
> fresh global variable that only the macro-introduced code can access.
> I'm not sure though that this is required for top-level variables.


My reading of R5RS 4.3 indicates that it is required. The macro in
question "inserts a binding for an identifier" which "will in effect be
renamed throughout its scope to avoid conflicts with other identifiers".
Guile is not doing this for top-level variables.

The subsequent sentence in 4.3 reads "Note that a define at top level
may or may not introduce a binding; see section 5.2". It doesn't say
that such definitions are exempt from the renaming behavior, though. Do
you have an alternative interpretation?

I'd be surprised to find that R5RS was intended to allow syntax-rules
macros to introduce their own top-level names, since it seems against
the spirit of the system. It means that it's possible for a macro to
inadvertently interfere with the value of a variable in its invoking
context.

> I don't have access to guile at the moment or I would've tested it like:
>
> (define-syntax test-hygiene
> (syntax-rules ()
> ((_ f)
> (begin
> (define hidden 42)
> (define (f) hidden)))))
>
> (let ()
> (test-hygiene foo)
> (list (foo) hidden))
>
> to see if hidden was introduced unhygienically.


Guile passes this test.

> Internal define-syntax is not required by r5rs.
>
> Section 5.3 (Syntax definitions) states:
> ``Syntax definitions are valid only at the top level of a <program>.''
> Also,
> `` There is no define-syntax analogue of internal definitions.''


Oh yeah, that rings a bell. :) I was really going for something more
like the test you wrote, but I was misled by the fact that my test
worked under PLT and various psyntax-based syntax-rules implementations.
The errors Guile produced confirmed my expectations.

Anton
Abdulaziz Ghuloum

2006-04-19, 7:06 pm

pschombe@uci.edu wrote:

>
> More importantly you shouldn't need it,


And you decided that on my behalf based on what exactly?

> at least I have never come
> across a problem that couldn't be solved without them,


Anybody can use that exact same argument for anything:

You shouldn't need macros also because ...
You shouldn't need continuations because ...
You shouldn't need higher-order functions because ...
You shouldn't need functions because ...
You shouldn't need high-level programming because ...
You shouldn't need low-level programming because ...

All this excuse does is justify your ignorance of something
that others have found very useful yet you ``have never came
across a problem that couldn't be solved without them.''

Aziz,,,
Abdulaziz Ghuloum

2006-04-19, 7:06 pm

Anton van Straaten wrote:

> Abdulaziz Ghuloum wrote:
>
>
>
> My reading of R5RS 4.3 indicates that it is required. The macro in
> question "inserts a binding for an identifier" which "will in effect be
> renamed throughout its scope to avoid conflicts with other identifiers".
> Guile is not doing this for top-level variables.
>
> The subsequent sentence in 4.3 reads "Note that a define at top level
> may or may not introduce a binding; see section 5.2". It doesn't say
> that such definitions are exempt from the renaming behavior, though. Do
> you have an alternative interpretation?


I would say that the macro in question "places a define at top level"
because that's what the macro expands to: (define hidden ...).

Now maybe guile takes the position that a define at top level does not
introduce a binding.

What follows is that the macro in question does not introduce a binding
to the top level.

Just a different interpretation I guess. I like psyntax's hygienic
top-level defines better because they're more general: you can always
use set! to modify existing top-level bindings. Only things you
*define* are renamed.

Aziz,,,
pschombe@uci.edu

2006-04-19, 7:06 pm

> You shouldn't need macros also because ...
> You shouldn't need continuations because ...
> You shouldn't need higher-order functions because ...
> You shouldn't need functions because ...
> You shouldn't need high-level programming because ...
> You shouldn't need low-level programming because ...


as an average programmer I use those things all the time. A beginner
in my opnion shouldn't need to know more than the average programmer.

Anton van Straaten

2006-04-19, 10:02 pm

Abdulaziz Ghuloum wrote:
> Anton van Straaten wrote:

....
>
>
> I would say that the macro in question "places a define at top level"
> because that's what the macro expands to: (define hidden ...).
>
> Now maybe guile takes the position that a define at top level does not
> introduce a binding.


But R5RS doesn't give complete freedom to make that choice. By 5.2.1,
"if <variable> is not bound, however, then the definition will bind
<variable> to a new location...". The exception to this is in those
implementations in which "all possible variables are bound to
locations". However, in such an implementation (e.g. Chicken), it must
be possible to successfully SET! a variable that does not have an
explicit definition. Guile does not allow such a use of SET!
Therefore, the kind of definition we're discussing does introduce a
binding, and a different argument would be needed to make the case for
Guile's behavior as being R5RS compliant.

> I like psyntax's hygienic
> top-level defines better because they're more general: you can always
> use set! to modify existing top-level bindings. Only things you
> *define* are renamed.


Right. This seems consistent with the intent of syntax-rules. To do
otherwise means that hygiene doesn't apply to top-level definitions,
which would be rather strange for a macro system billed as hygienic,
even taking into account the notoriously anything-goes nature of the top
level.

Anton
xscottg@gmail.com

2006-04-21, 7:07 pm

> [Jens Axel S=F8gaard listed several good references]

I got a chance to read those over last night. Good stuff. Thank you.


One more question though... As I wrote before, most of this was an
exercise in learning Scheme macros, but I was also curious if there was
a way to create/simulate a first class procedure that doesn't
immediately evaluate its arguments. That macro approach you guys have
helped me on is an aproximation, but the "procedures" it creates can't
be passed around in higher order functions since the lambda-izing
expansion happens too early. So it looks like creating lazy evaluation
procedures can't be done correctly without implementing a new
interpreter. Is there a way of doing this that I'm not aware of?

Jens Axel Søgaard

2006-04-21, 7:07 pm

xscottg@gmail.com wrote:
>
> I got a chance to read those over last night. Good stuff. Thank you.
>
> One more question though... As I wrote before, most of this was an
> exercise in learning Scheme macros, but I was also curious if there was
> a way to create/simulate a first class procedure that doesn't
> immediately evaluate its arguments.


Say for the sake of argument that lazy-lambda produces
procedure that evaluates its arguments lazily. What should
this example evaluate to?

(define foo
(lazy-lambda (x)
42)

(foo (/ 1 0))

Well since (<expr0> <expr1> ) is syntax for normal
function application the expression (foo (/ 1 0))
will be evaluated by evaluting the sub expressions
foo and (/ 1 0) first, and then applying the result
of the second to the result of the first - well, actually
during the evaluation of (/ 1 0) we will get an error,
and foo will never be called.

That is to apply a lazy procedure we need to use a specil
application, e.g.

(lazy-apply foo (/ 1 0))

In standard Scheme it is not possible to redefine the meaning of
(<expr1> <expr2> ). In PLT Scheme however (<expr1> <expr2> ) expands
to (#%app <expr1> <expr2> ), so by redefining #%app to lazy-apply
one can get the initial example to return 42.

> That macro approach you guys have
> helped me on is an aproximation, but the "procedures" it creates can't
> be passed around in higher order functions since the lambda-izing
> expansion happens too early. So it looks like creating lazy evaluation
> procedures can't be done correctly without implementing a new
> interpreter. Is there a way of doing this that I'm not aware of?


See the paper "Laziness Without All the Hard Work" by Eli Barzilay
and John Clements for details on the approach described above:

<http://www.ccs.neu.edu/scheme/pubs/fdpe05-bc.pdf>

See also:

<http://csu660.barzilay.org/lec20.txt>

--
Jens Axel Søgaard
<http://www.scheme.dk/blog/>
Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com