Is Scheme as good as Common Lisp?
How usable are Macros, anyway? Does their use tend to produce readable code?
How usable are Macros, anyway? Does their use tend to produce readable code?
However (a) all the Scheme implementations I know of have implemented classic defmacro macros as well, and (b) if one hadn't, you could easily write it yourself. So in practice there's not much of a decision to make.
The Arc implementation you're using to read this is written on top of Scheme. Currently Mzscheme.
What are the disadvantages? Is there any reason to dislike defining syntax rules?
For me the reason is just that defmacro is easier to read.
(defmacro aif (test then &optional else)
`(let ((it ,test))
(if it ,then ,else))) (cond ((assoc 'key collection)
=> (lambda (it) (do-something-with it)))
(else
(alternative)))
Alternatively, if you really wanted to create an AIF macro, you could use SYNTAX-RULES to create something that sugared up the above thusly: (aif it (assoc 'key collection)
(do-something-with it)
(alternative))
Or you could, going outside the standard, use SYNTAX-CASE (ugh, in R6RS) or one of the other non R5RS approaches to macros e.g. syntactic closures[1] that don't throw out the macro hygiene baby with the occasional slight inconvenience bath-water.I'm a bit of a reformed macrologist; if I get an idea for a cool macro, I try to see how far I can take a non-macrotic version and compare that to what the syntactically-sugared version would be like. Most of the time, I wind up not bothering with a macro. I put macros in the same category as EVAL: If you think you need to use a macro, think again.
http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z...
Macros are a build-your-own-language construct, but to a certain extent so are functions. I agree that one should try functions first, but would not go so far as to put macros in the same category as using eval.
I should have been more detailed and noted that on the "by all means use it" vs. "if you think you need it, think again" continuum, EVAL requires IMO more justification than a macro. The source of my concern over macros is that when designing them, care needs to be taken to clearly communicate their semantics, which is best done IMO by following the established conventions of the language and whatever other macros the user is expected to be familiar with.
Someone could give a "trust the programmer" argument for making unhygienic macros available in a language, but that nostrum is problematic because there are two programmers: the macro writer and the macro user. It's harder than you think to write a macro using DEFMACRO that always behaves the way a user might expect it to behave, and as a user of a macro, you need to wonder how hard the macro writer worked to protect you -- something that I don't like as a fan of black boxes.
(require (lib "defmacro.ss"))
(define-macro (aif a b c)
`(let ((it ,a))
(if it ,b ,c)))
(aif (* 10 10) (+ it 1) 'oops) ==> 101
The "aif" form makes it so you don't have to create a temporary variable name first -- the temp var is just assumed to be named "it". If you're thinking, "whatever, I don't need that" you are wrong. :) Somehow this turns into the most wonderful little line-saver. At my last company, we used it all over the place.
If I didn't create a string-case= macro, I would have to write the final expression over and over again. With multiple string-case matches I would have to write string=? many many times:
(cond ((or (string=? "hello" my-string)
(string=? "world" my-string))
(print "match")))
To do that with the string-case= macro: (string-case= my-string
(("hello" "world")
(print "match")))
Saves quite a bit of typing and now I have a good example of why macros are good to have around :Dedit: formatting ftw.
(cond ((or (string=? "hello" my-string)
(string=? "world" my-string))
(print "match")))
(string-case= my-string
(("hello" "world") (print "match")))
(define (string-case= my-string . clauses)
(cond ((null? clauses) 'whatever)
((string-member? my-string (caar clauses))
((cadar clauses)))
(else (apply string-case=
my-string
(cdr clauses)))))
Where string-member? is trivial to define as a procedure too.Then you use it like
(string-case=
('("hello" "world")
(lambda () (print "match"))))
I myself wouldn't use a macro for this little gain.[Edit: I had completely misunderstood the macro.]
Use of macros makes the code reflect the programmer's thinking about the problem domain. Whether this is readable or not depends on the programmer.