; Say that Scheme did not have OR, and we wanted to add it using IF. Recall ; that OR short-circuits, so it does not evaluation the 2nd expression unless ; it has to. It should also evaluate the 1st argument exactly once. Here's ; a first try: (define (OR1 a-val b-val) (if a-val a-val b-val)) ; If we define some functions: (define (slow-but-true) (print "Running slow-function") (newline) #t) (define (crash) (error "BANG!")) ; and then test it out: ; ; (OR1 (slow-but-true) (crash)) ; ; we'd find that eager evaluation caused us to run (crash), which crashes ; our program. So we need to use a macro to delay evaluation of b: (define-syntax-rule (OR2 ) (if )) ; Let's test that out: ; ; (OR2 (slow-but-true) (crash)) ; ; We'll find that we got the right answer but had to run slow-but-true twice, ; which is inefficient (and potentially wrong in a world with mutation). ; ; Here's a third attempt that fixes this: (define-syntax-rule (OR3 ) (let ([a-val ]) (if a-val a-val ))) ; This one works correctly. But... what about the magic name a-val that we ; introduced? What if one of the expressions contained a variable named ; a-val? Here's an example: (let ([a-val 5]) (OR3 #f a-val)) ; This prints 5 instead of #f. That's what we wanted, but how did it work? ; The answer is that Scheme's macro system is hygienic. It marks each ; variable name with the level of evaluation at which it was introduced. ; Variables in "raw code" are at level 0. Variables introduced by macros ; are at the current level, plus 1. So even though they use the same names, ; when variables are extended by the level that they were marked by they are ; different as far as the environment is concerned. This also means that ; macros can be nested without interfering with each other. It is occasionally ; annoying that this behavior occurs, e.g., it might be nice to do: ; ; (with-math-constants ; (+ sqrt2 pi e golden-ratio)) ; ; However, that's the same argument as saying "occasionally dynamic scope is ; useful." In general, a non-hygienic macro system makes code very hard to ; understand for humans and very hard for an optimizing compiler to reason ; about. So we sacrifice the handful of times when it is useful for the ; majority of times when it is too dangerous.