Fear of Macros

Fear of Macros

1 Preface
I learned Racket after 25 years of mostly using C and C++.
Some psychic whiplash resulted.
"All the parentheses" was actually not a big deal. Instead, the first +
1 Preface
I learned Racket after 25 years of mostly using C and C++.
Some psychic whiplash resulted.
"All the parentheses" was actually not a big deal. Instead, the first mind warp was functional programming. Before long I wrapped my brain around it, and went on to become comfortable and effective with many other aspects and features of Racket.
But two final frontiers remained: Macros and continuations.
I found that simple macros were easy and understandable, plus there @@ -47,16 +47,17 @@ similar for macro. There is. One of the more-recent Racket macro enhancements is syntax-parse.
3 Transformers
YOU ARE INSIDE A ROOM. |
THERE ARE KEYS ON THE GROUND. |
THERE IS A SHINY BRASS LAMP NEARBY. |
|
IF YOU GO THE WRONG WAY, YOU WILL BECOME |
HOPELESSLY LOST AND CONFUSED. |
|
> pick up the keys |
|
YOU HAVE A SYNTAX TRANSFORMER |
3.1 What is a syntax transformer?
A syntax transformer is not one of the トランスフォーマ transformers.
Instead, it is simply a function. The function takes syntax and returns syntax. It transforms syntax.
Here’s a transformer function that ignores its input syntax, and -always outputs syntax for a string literal:
> (define-syntax foo (lambda (stx) #'"I am foo"))
Using it:
> (foo) "I am foo"
When we use define-syntax, we’re making a transformer +always outputs syntax for a string literal:
> (define-syntax foo (lambda (stx) (syntax "I am foo")))
Using it:
> (foo) "I am foo"
When we use define-syntax, we’re making a transformer binding. This tells the Racket compiler, "Whenever you encounter a chunk of syntax starting with foo, please give it to my transformer function, and replace it with the syntax I give back to you." So Racket will give anything that looks like (foo ...) to our function, and we can return new syntax to use -instead. Much like a search-and-replace.
Maybe you know that the usual way to define a function in Racket:
is shorthand for:
That shorthand lets you avoid typing lambda and some parentheses.
Well there is a similar shorthand for define-syntax:
> (define-syntax (also-foo stx) #'"I am also foo") > (also-foo) "I am also foo"
What we want to remember is that this is simply shorthand. We are +instead. Much like a search-and-replace.
Maybe you know that the usual way to define a function in Racket:
is shorthand for:
That shorthand lets you avoid typing lambda and some parentheses.
Well there is a similar shorthand for define-syntax:
> (define-syntax (also-foo stx) (syntax "I am also foo")) > (also-foo) "I am also foo"
What we want to remember is that this is simply shorthand. We are still defining a transformer function, which takes syntax and returns syntax. Everything we do with macros, will be built on top of this basic idea. It’s not magic.
Speaking of shorthand, there is also a shorthand for syntax, -which is #’:
> (define-syntax (quoted-foo stx) #'"I am also foo, using #' instead of syntax") > (quoted-foo) "I am also foo, using #' instead of syntax"
Of course, we can emit syntax that is more interesting than a +which is #’:
> (define-syntax (quoted-foo stx) #'"I am also foo, using #' instead of syntax") > (quoted-foo) "I am also foo, using #' instead of syntax"
We’ll use the #’ shorthand from now on.
Of course, we can emit syntax that is more interesting than a string literal. How about returning (displayln "hi")?
> (define-syntax (say-hi stx) #'(displayln "hi")) > (say-hi) hi
When Racket expands our program, it sees the occurrence of (say-hi), and sees it has a transformer function for that. It calls our function with the old syntax, and we return the new syntax, @@ -245,7 +246,9 @@ heavily on that, mostly just updating it since his post was written before PLT Scheme was renamed to Racket.
After initially wondering if I was asking the wrong question and conflating two different issues :), Shriram Krishnamurthi looked at an early draft and encouraged me to keep going. Sam Tobin-Hochstadt and -Robby Findler also encouraged me.
Finally, I noticed something strange. After writing much of this, when +Robby Findler also encouraged me. Matthew Flatt showed me how to make +a Scribble interaction print syntax as +"syntax" rather than as "#'".
Finally, I noticed something strange. After writing much of this, when I returned to some parts of the Racket documentation, I noticed it had improved since I last read it. Of course, it was the same. I’d changed. It’s interesting how much of what we already know is diff --git a/main.rkt b/main.rkt index 2cd6861..acaa56f 100644 --- a/main.rkt +++ b/main.rkt @@ -2,7 +2,9 @@ @(require racket/sandbox scribble/eval + scribble/racket racket/date + (for-syntax racket/base) (for-label racket) (for-label racket/stxparam) (for-label syntax/parse)) @@ -136,11 +138,15 @@ returns syntax. It transforms syntax. Here's a transformer function that ignores its input syntax, and always outputs syntax for a string literal: +@(let-syntax([syntax (make-element-id-transformer + (lambda (stx) + #'@racket[syntax]))]) ;print as syntax not #' @i[ (define-syntax foo (lambda (stx) (syntax "I am foo"))) ] +) Using it: @@ -168,11 +174,15 @@ That shorthand lets you avoid typing @racket[lambda] and some parentheses. Well there is a similar shorthand for @racket[define-syntax]: +@(let-syntax([syntax (make-element-id-transformer + (lambda (stx) + #'@racket[syntax]))]) ;print as syntax not #' @i[ (define-syntax (also-foo stx) (syntax "I am also foo")) (also-foo) ] +) What we want to remember is that this is simply shorthand. We are still defining a transformer function, which takes syntax and returns @@ -182,12 +192,17 @@ basic idea. It's not magic. Speaking of shorthand, there is also a shorthand for @racket[syntax], which is @tt{#'}: +@margin-note{@tt{#'} is short for @racket[syntax] much like +@tt{'} is short for @racket[quote].} + @i[ (define-syntax (quoted-foo stx) #'"I am also foo, using #' instead of syntax") (quoted-foo) ] +We'll use the #' shorthand from now on. + Of course, we can emit syntax that is more interesting than a string literal. How about returning @racket[(displayln "hi")]? @@ -958,7 +973,9 @@ before PLT Scheme was renamed to Racket. After initially wondering if I was asking the wrong question and conflating two different issues :), Shriram Krishnamurthi looked at an early draft and encouraged me to keep going. Sam Tobin-Hochstadt and -Robby Findler also encouraged me. +Robby Findler also encouraged me. Matthew Flatt showed me how to make +a Scribble @racket[interaction] print @racket[syntax] as +@racket["syntax"] rather than as @racket["#'"]. Finally, I noticed something strange. After writing much of this, when I returned to some parts of the Racket documentation, I noticed it had