I'm rather new to Lisp & Scheme (< 3 months total exposure) but I'm trying to make a real effort to learn the language. I'm delving into macros and having a problem understanding the basics.
I've defined a macro called print-line like so:
(define-macro (print-line msg) (list `(display ,msg) `(newline)))
When I try to use the macro from the repl I get the error message:
(print-line "hi") hi ***ERROR in (console)@38.1 -- Operator is not a PROCEDURE
Adam gave a good explanation of this error and how to solve it. Another issue for a new Scheme programmer is *when* to use a macro.
In this example there is one argument and generally you would expect that argument to be evaluated before printing. Simple procedures serve this purpose just fine, so the preferred solution would be something like...
(define (print-line msg) (display msg) (newline))
This may be old news to you, and you're just using print-line as an example. But I have run into so much code over the years where programmers chose macros where procedures would be simpler.
I choose to implement a macro primarily when I need to control the order of evaluation, as in a control structure like if, while, etc.
As an example an old Lisp control structure is called PROG1. This structure takes a sequence of statements, evaluates each in order, and returns the result of the first statement after the last has been evaluated. This is can be expressed as a Gambit macro, but I'll call it begin1 to be more like Scheme's begin than the old Lisp's progn.
(define-macro (begin1 first-statement . remaining-statements)
(let ((result-var (gensym 'first-result))) `(let ((,result-var ,first-statement)) ,@remaining-statements ,result-var)))
(begin1 1 2 3)
1
(begin 1 2 3)
3
(define result-var 5) (begin 1 2 result-var)
5
(begin result-var 1 2 3)
3
(begin1 result-var 1 2 3)
5
(begin1 0 (display result-var) (newline) 1 2 3)
5 0
Then the other nasty thing to watch out for when playing with control structures is the inadvertent conflict of variables introduced by the macro and those in the original statements. And so gensym is used to introduce uniquely named symbols in the macro.
-Patrick
Afficher les réponses par date
Logan, Patrick D wrote:
I'm rather new to Lisp & Scheme (< 3 months total exposure) but I'm trying to make a real effort to learn the language. I'm delving into macros and having a problem understanding the basics.
I've defined a macro called print-line like so:
(define-macro (print-line msg) (list `(display ,msg) `(newline)))
When I try to use the macro from the repl I get the error message:
(print-line "hi") hi ***ERROR in (console)@38.1 -- Operator is not a PROCEDURE
Adam gave a good explanation of this error and how to solve it. Another issue for a new Scheme programmer is *when* to use a macro.
In this example there is one argument and generally you would expect that argument to be evaluated before printing. Simple procedures serve this purpose just fine, so the preferred solution would be something like...
(define (print-line msg) (display msg) (newline))
This may be old news to you, and you're just using print-line as an example. But I have run into so much code over the years where programmers chose macros where procedures would be simpler.
I choose to implement a macro primarily when I need to control the order of evaluation, as in a control structure like if, while, etc.
As an example an old Lisp control structure is called PROG1. This structure takes a sequence of statements, evaluates each in order, and returns the result of the first statement after the last has been evaluated. This is can be expressed as a Gambit macro, but I'll call it begin1 to be more like Scheme's begin than the old Lisp's progn.
(define-macro (begin1 first-statement . remaining-statements)
(let ((result-var (gensym 'first-result))) `(let ((,result-var ,first-statement)) ,@remaining-statements ,result-var)))
(begin1 1 2 3)
1
(begin 1 2 3)
3
(define result-var 5) (begin 1 2 result-var)
5
(begin result-var 1 2 3)
3
(begin1 result-var 1 2 3)
5
(begin1 0 (display result-var) (newline) 1 2 3)
5 0
Then the other nasty thing to watch out for when playing with control structures is the inadvertent conflict of variables introduced by the macro and those in the original statements. And so gensym is used to introduce uniquely named symbols in the macro.
-Patrick
I have On Lisp queued up to read in the very near future. I'm currently using Graham's ANSI Common Lisp, the R5RS spec, and Gambit's doc as I slowly climb the learning curve.
In your opinion, is it appropriate to use a macro to abstract away repetitive boiler-plate code? Or is this better done in a procedure?
--Kevin