views:

143

answers:

5

I often find the following type of incremental definition useful:

(define (foo) (display "bar"))
(foo)
;prints bar
(define foo (let ((bar foo))
              (lambda ()
                (display "foo")
                (bar))))

(foo)
;prints foobar

How do I preform this type of incremental definition with macros? I could not get let-syntax to provide the same functionality.

Currently I use plt scheme, but would like to see answers in different lisp implementations as well.

Edit:

Naively I would want to do the following:

(define-syntax foo
  (syntax-rules ()
    ((_) (display "bar"))))

(define-syntax foo
  (let-syntax ((old-foo (syntax-rules () ((_) (foo)))))
    (syntax-rules ()
      ((_) (begin
             (display "foo")
             (old-foo))))))

Translation of naive macros to working plt scheme macros:

(require-for-syntax scheme/base) 
(define-syntax foo
  (syntax-rules ()
    [(foo) (display "bar")]))
(define-syntax foo
  (let ([old (syntax-local-value #'foo)])
    (lambda (stx)
      #`(begin #,((syntax-rules ()
               [(_) (begin (display "foo"))]) stx)
             #,(old #'(_))))))
(foo)

If I am missing a better method let me know.

+4  A: 

With macros doing this you are on the way to create the hardest to maintain software on the planet.

Edit: in Common Lisp it is possible. I can't remember that I have ever seen it used in source code.

Adding behavior to functions or macros is often called 'advise' or 'advice' in the Common Lisp community. Some 'advise' tools have also the ability to advise macros.

Rainer Joswig
Currently nothing I program with scheme has anything to do with mission critical or even my personal work flow. Also the incremental definition I have above could easily become a python like function decorator. So another way of wording my question would be are macro decorators possible?
Davorak
@Davorak: do you decorate decorators?
outis
In python decorators can be used on the decorator's definition while I have not had a chance to use them in that fashion in my own code I do see how it could be useful to do so. Just to make sure my previous comment was clear what I meant by macro decorator was a macro which could be used to decorate another macro.
Davorak
@Rainer Joswig Thank you I will look around for advise tools in CL.
Davorak
Davorak: decorators in python are not related to macros, they're similar to advice.
Eli Barzilay
+4  A: 

I do not think that you can do something like this with macros. I also do not see any point in trying.

Please note that macros are not just some functions with extra magic! Macros are something different altogether.

Perhaps you are looking for something like method combinations in Common Lisp?

Svante
Thank you. Do you also not see the point in the functional case? Or the case for decorators in python? Is it useful nowhere? Or why is it not useful for macros in particular?...Now that I have read a little about method combinations they seem to provide the same functionality of decorators in python. Why is this level of abstraction useful for functions but pointless for macros?
Davorak
@Davorak: decorators are distinct in functionality from method combinations. Decorators are executed at definition, while combined methods are executed when the method is called. The latter are an essential part of Aspect Oriented Programming. If anything, decorators are more similar to macros, but used in more limited circumstances.
outis
What you seem to describe is a implementation difference not a functionality difference. I can use a decorator to run extra code before, after, or around a function or completely override it. This is just like method combinations in my understanding from reading the spec, that you can use a method combination to run extra code before, after, or around an existing function.
Davorak
@Davorak: there is a decided functional difference, in addition to differences of purpose. Decorators are evaluated a single time for each definition. When a method decorator is evaluated, the class isn't yet defined, let alone an object. When method combinations are evaluated (at method invocation), they have an object to access. Lisp macros are evaluated at their own special macro-expansion time, before functions are run. This is what allows macros to be used for features like lazy evaluation, or conditional evaluation. Macros let you extend the language, rather than perform a calculation.
outis
@outis: You are correct and I was not being clear. If you want run time invocation using python decorators you end up wrapping the decorated function with your own function that calls decorated function. With this method it is possible to run extra code after, before, or around a decorated function at method invocation.
Davorak
@Davorak: true, you can implement advice using decorators. It wouldn't be too hard to write `@before`, `@after` and `@around` decorators that take methods and turn them into advice on the method. Decorators themselves are more than method combinations.
outis
It's true that there's not much point in doing this, but it's wrong to say that macros are not functions -- at least in Lisp and in most Schemes they certainly are.
Eli Barzilay
@Eli Barzilay: Sorry for the imprecise language. You are invited to explain the fundamental difference in a better concise yet precise manner.
Svante
Svante: the difference is in the machine that revolves around macros -- how they are bound, how they are invoked, etc.
Eli Barzilay
A: 

What about let*?

(define foobar
    (let* ((foo (lambda () (display "foo")))
           (bar (lambda () (foo) (display "bar"))) )
            bar))
jdeseno
Thank you, but I think that you misunderstood the question. I was interested in being able to change the definition of the global variable while the above is only local. Also, my question was how it it possible to do with macros.
Davorak
+1  A: 

I think you could get this by using unhygienic macros, which I believe PLT Scheme supports. Then you would use exactly the same mechanisms as with regular functions, since the macros would be regular functions that happened to operate on S-expressions.

I don't know how to do this with hygienic macros, but I'm quite surprised you can't - I would consider asking on a PLT mailing list.

Noah Lavine
Thank you, I will try asking the mailing list at some point. I would still be surprised if it possible with unhygienic macros in plt, but pleased. I have tried to make a answer with defmacro, but have not able to figure an answer out, though I am no expert on defmacro.
Davorak
It *is* possible to do in PLT -- they key is not with macros that are hygienic or not, but with a procedural macro layer and some reflective capabilities that go with it.
Eli Barzilay
+1  A: 

FWIW (and this is definitely not much, since this is pretty much an exercise in target practicing with your feet), here is how you would do this in PLT Scheme with only hygienic syntax-rules macros:

(define-syntax foo
  (syntax-rules ()
    [(foo x) (list 'x '= x)]))
(define-syntax foo
  (let ([old (syntax-local-value #'foo)])
    (compose (syntax-rules ()
               [(_ x ...) (list 'begin x ... 'end)])
             old)))
(printf ">>> ~s\n" (foo (+ 1 2)))

This will not work inside a module, only on the REPL -- and that is a good thing. It is possible to do something similar in modules too, but if you're going for that, you can just as well use procedural macros (aka syntax-case macros), with an identifier that is bound at the syntax level and `set!'-ing its value to extend it. Still not a great idea, and can still lead to eyes bleeding, but some people like to hurt themselves...

(Oh, and BTW -- even doing this is still completely unrelated to whether the macros in question are hygienic or not.)

Eli Barzilay
Thank you, I had to include (require-for-syntax scheme/base) to get compose to be defined in the syntax phase?(To make compose defined within define-syntax). Can you comment on my naive approach(added to question) is not implemented? Weather it is not practical or how it is inconsistant with existing syntax?
Davorak
Yes, you need `scheme/base` to get compose; but you can combine macro functions any way you want -- they're just functions from syntax to syntax, and `syntax-rules` is a convenient (2nd-level, usually) macro that generates such functions.As for your naive approach, it suffers from some fundamental problems: it tries to somehow capture the old syntax, but you cannot get its *value* this way, so the result is a macro that expands to calls to itself. Another problem is in trying to use `display` to debug stuff -- macros won't *do* it, they will *expand* to it.
Eli Barzilay
Thanks, I knew I could not capture the old syntax in this fashion and that it would expand into itself. I just thought it nicely parelled a syntax I was familiar with and wondered why since it existed at the function level why did it not exist at the macro level even though there did not seem to be anything fundamental stopping a compiler from acting that way. I was trying to use display as a test to see if the macro was constructed properly, I edited my question a translation of my naive macro to a working plt scheme macro.
Davorak
Your new version still has lots of problems, but I'm afraid that it's not going to be effective to describe them in these comments...
Eli Barzilay
Thank you for your insight and for trying to clear up the misconceptions that cropped up in the various answers.
Davorak