Fear of Macros

Contents:
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 +
Fear of Macros

Contents:
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 @@ -209,7 +209,7 @@ a template, and its value will go in the template.
We might have one more& Let’s try to use our new version:
> (hyphen-define/wrong1.2 foo bar () #t) > (foo-bar) foo-bar: undefined;
cannot reference an identifier before its definition
in module: 'program
Hmm. foo-bar is still not defined. Back to the Macro Stepper. It says now we’re expanding to:
(define (|#<syntax:11:24foo>-#<syntax:11:28 bar>|) #t)
Oh right: #'a and #'b are syntax objects. Therefore
(string->symbol (format "~a-~a" #'a #'b))
is the printed form of both syntax objects, joined by a hyphen:
|#<syntax:11:24foo>-#<syntax:11:28 bar>|
Instead we want the datum in the syntax objects, such as the symbols foo and bar. Which we get using -syntax->datum:
> (define-syntax (hyphen-define/ok1 stx) (syntax-case stx () [(_ a b (args ...) body0 body ...) (syntax-case (datum->syntax stx (string->symbol (format "~a-~a" (syntax->datum #'a) (syntax->datum #'b)))) () [name #'(define (name args ...) body0 body ...)])])) > (hyphen-define/ok1 foo bar () #t) > (foo-bar) #t
And now it works!
Now for two shortcuts.
Instead of an additional, nested syntax-case, we could use +syntax->datum:
> (define-syntax (hyphen-define/ok1 stx) (syntax-case stx () [(_ a b (args ...) body0 body ...) (syntax-case (datum->syntax stx (string->symbol (format "~a-~a" (syntax->datum #'a) (syntax->datum #'b)))) () [name #'(define (name args ...) body0 body ...)])])) > (hyphen-define/ok1 foo bar () #t) > (foo-bar) #t
And now it works!
4.1.1 with-syntax
Now for two shortcuts.
Instead of an additional, nested syntax-case, we could use
with-syntaxAnother name for
with-syntax could be, "define pattern variable".. This
rearranges the syntax-case to look more like a let
@@ -217,7 +217,7 @@ statement—
> (define-syntax (hyphen-define/ok2 stx) (syntax-case stx () [(_ a b (args ...) body0 body ...) (with-syntax ([name (datum->syntax stx (string->symbol (format "~a-~a" (syntax->datum #'a) (syntax->datum #'b))))]) #'(define (name args ...) body0 body ...))])) > (hyphen-define/ok2 foo bar () #t) > (foo-bar) #t
Whether you use an additional syntax-case or use with-syntax, either way you are simply defining an additional pattern variable. Don’t let the terminology and structure make it seem -mysterious.
Also, there is a utility function in racket/syntax called +mysterious.
4.1.2 format-id
Also, there is a utility function in racket/syntax called format-id that lets us format identifier names more succinctly. As we’ve learned, we need to require the module using for-syntax, since we need it at compile time:
> (require (for-syntax racket/syntax))
> (define-syntax (hyphen-define/ok3 stx) (syntax-case stx () [(_ a b (args ...) body0 body ...) (with-syntax ([name (format-id stx "~a-~a" #'a #'b)]) #'(define (name args ...) body0 body ...))])) > (hyphen-define/ok3 bar baz () #t) > (bar-baz) #t
Using format-id is convenient as it handles the tedium of diff --git a/main.rkt b/main.rkt index 1163a15..6951a8c 100644 --- a/main.rkt +++ b/main.rkt @@ -837,6 +837,8 @@ Instead we want the datum in the syntax objects, such as the symbols And now it works! +@subsubsection{@racket[with-syntax]} + Now for two shortcuts. Instead of an additional, nested @racket[syntax-case], we could use @@ -865,6 +867,8 @@ Whether you use an additional @racket[syntax-case] or use pattern variable. Don't let the terminology and structure make it seem mysterious. +@subsubsection{@racket[format-id]} + Also, there is a utility function in @racket[racket/syntax] called @racket[format-id] that lets us format identifier names more succinctly. As we've learned, we need to @racket[require] the module