diff --git a/gh.css b/gh.css index 3151790..9c6b8a5 100644 --- a/gh.css +++ b/gh.css @@ -53,6 +53,11 @@ font-family: sans-serif; } +h3, h4, h5, h6, h7, h8 { + margin-top: 1.75em; + margin-bottom: 0.5em; + font-size: 100%; +} /* ---------------------------------------- */ /* Table of contents, inline */ diff --git a/index.html b/index.html index a74159c..006ba21 100644 --- a/index.html +++ b/index.html @@ -1,6 +1,6 @@ Fear of Macros
1 Preface
2 Our plan of attack
3 Transform!
3.1 What is a syntax transformer?
3.2 What’s the input?
3.3 Actually transforming the input
3.4 Compile time vs. run time
3.5 begin-for-syntax
4 Pattern matching: syntax-case and syntax-rules
4.1 Pattern variable vs. template—fight!
4.1.1 with-syntax
4.1.2 with-syntax*
4.1.3 format-id
4.1.4 Another example
4.2 Making our own struct
4.3 Using dot notation for nested hash lookups
5 Syntax parameters
6 What’s the point of splicing-let?
7 Robust macros: syntax-parse
7.1 Error-handling strategies for functions
7.2 Error-handling strategies for macros
7.3 Using syntax/ parse
8 References and Acknowledgments
9 Epilogue

Fear of Macros

-
Copyright (c) 2012 by Greg Hendershott. All rights reserved.
Last updated 2012-11-12 23:13:02
Feedback and corrections are welcome here.

Contents:

    1 Preface

    2 Our plan of attack

    3 Transform!

      3.1 What is a syntax transformer?

      3.2 What’s the input?

      3.3 Actually transforming the input

      3.4 Compile time vs. run time

      3.5 begin-for-syntax

    4 Pattern matching: syntax-case and syntax-rules

      4.1 Pattern variable vs. template—fight!

        4.1.1 with-syntax

        4.1.2 with-syntax*

        4.1.3 format-id

        4.1.4 Another example

      4.2 Making our own struct

      4.3 Using dot notation for nested hash lookups

    5 Syntax parameters

    6 What’s the point of splicing-let?

    7 Robust macros: syntax-parse

      7.1 Error-handling strategies for functions

      7.2 Error-handling strategies for macros

      7.3 Using syntax/parse

    8 References and Acknowledgments

    9 Epilogue

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 +

Copyright (c) 2012 by Greg Hendershott. All rights reserved.
Last updated 2012-11-13 12:56:57
Feedback and corrections are welcome here.

Contents:

    1 Preface

    2 Our plan of attack

    3 Transform!

      3.1 What is a syntax transformer?

      3.2 What’s the input?

      3.3 Actually transforming the input

      3.4 Compile time vs. run time

      3.5 begin-for-syntax

    4 Pattern matching: syntax-case and syntax-rules

      4.1 Pattern variable vs. template—fight!

        4.1.1 with-syntax

        4.1.2 with-syntax*

        4.1.3 format-id

        4.1.4 Another example

      4.2 Making our own struct

      4.3 Using dot notation for nested hash lookups

    5 Syntax parameters

    6 What’s the point of splicing-let?

    7 Robust macros: syntax-parse

      7.1 Error-handling strategies for functions

      7.2 Error-handling strategies for macros

      7.3 Using syntax/parse

    8 References and Acknowledgments

    9 Epilogue

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 @@ -212,15 +212,15 @@ Stepper. It says now we’re expanding to:

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!

Next, some shortcuts.

4.1.1 with-syntax

Instead of an additional, nested syntax-case, we could use with-syntaxAnother name for -with-syntax could be, "define pattern variable".. This +with-syntax could be, "with new pattern variable".. This rearranges the syntax-case to look more like a let statement—first the name, then the value. Also it’s more convenient -if we need to define more than one pattern variable.

> (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.

4.1.2 with-syntax*

We may recall that let doesn’t let us use a definition in a -subsequent clause:

> (let ([a 0]
        [b a])
    (values a b))

a: undefined;

 cannot reference an identifier before its definition

  in module: 'program

We could nest lets:

> (let ([a 0])
    (let ([b a])
      (values a b)))

0

0

Or we could use let*:

> (let* ([a 0]
         [b 0])
    (values a b))

0

0

Similarly there is a with-syntax* variation of -with-syntax:

> (require (for-syntax racket/syntax))
> (define-syntax (foo stx)
    (syntax-case stx ()
      [(_ a)
        (with-syntax* ([b #'a]
                       [c #'b])
          #'c)]))

One gotcha is that with-syntax* isn’t provided by +if we need to define more than one pattern variable.

> (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

Again, with-syntax is simply syntax-case rearranged:

(syntax-case <syntax> () [<pattern> <body>])
(with-syntax ([<pattern> <syntax>]) <body>)

Whether you use an additional syntax-case or use +with-syntax, either way you are simply defining additional +pattern variables. Don’t let the terminology and structure make it +seem mysterious.

4.1.2 with-syntax*

We know that let doesn’t let us use a binding in a subsequent +one:

> (let ([a 0]
        [b a])
    b)

a: undefined;

 cannot reference an identifier before its definition

  in module: 'program

Instead we can nest lets:

> (let ([a 0])
    (let ([b a])
      b))

0

Or use a shorthand for nesting, let*:

> (let* ([a 0]
         [b a])
    b)

0

Similarly, instead of writing nested with-syntaxs, we can use +with-syntax*:

> (require (for-syntax racket/syntax))
> (define-syntax (foo stx)
    (syntax-case stx ()
      [(_ a)
        (with-syntax* ([b #'a]
                       [c #'b])
          #'c)]))

One gotcha is that with-syntax* isn’t provided by racket/base. We must (require (for-syntax racket/syntax)). Otherwise we may get a rather bewildering error message:

...: ellipses not allowed as an expression in: ....

4.1.3 format-id

There is a utility function in racket/syntax called format-id that lets us format identifier names more diff --git a/main.rkt b/main.rkt index fc837b8..e4b97f3 100644 --- a/main.rkt +++ b/main.rkt @@ -845,7 +845,7 @@ Next, some shortcuts. Instead of an additional, nested @racket[syntax-case], we could use @racket[with-syntax]@margin-note*{Another name for -@racket[with-syntax] could be, "define pattern variable".}. This +@racket[with-syntax] could be, "with new pattern variable".}. This rearranges the @racket[syntax-case] to look more like a @racket[let] statement---first the name, then the value. Also it's more convenient if we need to define more than one pattern variable. @@ -864,40 +864,47 @@ if we need to define more than one pattern variable. (foo-bar) ] +Again, @racket[with-syntax] is simply @racket[syntax-case] rearranged: + +@racketblock[ +(syntax-case #,(italic "") () [#,(bold "") ]) +(with-syntax ([#,(bold "") #,(italic "")]) ) +] + Whether you use an additional @racket[syntax-case] or use -@racket[with-syntax], either way you are simply defining an additional -pattern variable. Don't let the terminology and structure make it seem -mysterious. +@racket[with-syntax], either way you are simply defining additional +pattern variables. Don't let the terminology and structure make it +seem mysterious. @subsubsection{@racket[with-syntax*]} -We may recall that @racket[let] doesn't let us use a definition in a -subsequent clause: +We know that @racket[let] doesn't let us use a binding in a subsequent +one: @i[ (let ([a 0] [b a]) - (values a b)) + b) ] -We could nest @racket[let]s: +Instead we can nest @racket[let]s: @i[ (let ([a 0]) (let ([b a]) - (values a b))) + b)) ] -Or we could use @racket[let*]: +Or use a shorthand for nesting, @racket[let*]: @i[ (let* ([a 0] - [b 0]) - (values a b)) + [b a]) + b) ] -Similarly there is a @racket[with-syntax*] variation of -@racket[with-syntax]: +Similarly, instead of writing nested @racket[with-syntax]s, we can use +@racket[with-syntax*]: @i[ (require (for-syntax racket/syntax))