4 ms·
For me, one of the simplest explanations of the difference between macros and functions is the short-circuiting and. In many languages, and does not evaluate a
by jarcane 12y ago
For me, one of the simplest explanations of the difference between macros and functions is the short-circuiting and.
In many languages, and does not evaluate all of its arguments, only as many as it needs to until it finds #f. This sounds like an easy thing to implement, but in most languages it actually isn't, because order of nested operations means that without being asked, it will evaluate it's arguments before our and.
So we can write this broken-and easily enough:
(define (broken-and . rest)
(cond
[(null? rest) #t]
[(car rest) (apply broken-and (cdr rest))]
[else #f]))
But give it the following, and you get an error:
> (broken-and (zero? 0) (string? "dave") (null? '(is fat)) (/ 5 0))
. . /: division by zero
And that will work so long as all your args are valid. But what will work better is to write a macro. Macros aren't evaluated, they merely splice in their syntax, but we can write them a lot like code, using all our control structures and so forth, and in Scheme/Racket, also some cool pattern matching stuff):
(define-syntax my-and.v2
(syntax-rules ()
[(_) #t]
[(_ a) a]
[(_ a b ...) (if a
(my-and.v2 b ...)
#f)]))
Now we can try our input from before, and get what we want, because the macro expands recursively and won't ever get to our divide by zero, at least unless all preceding args are true:
> (my-and.v2 (zero? 0) (string? "dave") (null? '(is fat)) (/ 5 0))
#f
This property of only expanding, not evaluating, is why macros can write all kinds of clever control structures, because you're mangling syntax, not evaluating code.