diff --git a/index.html b/index.html index 7fa77ba..2726a3f 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 format-id
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-08 16:16:21
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 format-id

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

Fear of Macros

+
Copyright (c) 2012 by Greg Hendershott. All rights reserved.
Last updated 2012-11-12 19:48:10
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 format-id

        4.1.3 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 @@ -84,7 +84,7 @@ given:

> (syntax->datum #'(reverse-me "backwards" "am" "i" values))

'(reverse-me "backwards" "am" "i" values)

Using cdr slices off the first item of the list, reverse-me, leaving the remainder: ("backwards" "am" "i" values). Passing that to -reverse changes it to (values "i" "am" "backwards"):

> (reverse (cdr '("backwards" "am" "i" values)))

'(values "i" "am")

Finally we use syntax->datum to convert this back to +reverse changes it to (values "i" "am" "backwards"):

> (reverse (cdr '(reverse-me "backwards" "am" "i" values)))

'(values "i" "am" "backwards")

Finally we use datum->syntax to convert this back to syntax:

> (datum->syntax #f '(values "i" "am" "backwards"))

#<syntax (values "i" "am" "backwards")>

That’s what our transformer function gives back to the Racket compiler, and that syntax is evaluated:

> (values "i" "am" "backwards")

"i"

"am"

"backwards"

3.4 Compile time vs. run time

(define-syntax (foo stx)
  (make-pipe) ;Ce n'est pas le temps d'exécution
  #'(void))

Normal Racket code runs at ... run time. Duh.

Instead of "compile time vs. run time", you may hear it described as "syntax phase vs. runtime phase". Same difference.

But a syntax transformer is called by Racket as part of the process of @@ -223,8 +223,8 @@ mysterious.

4.1.2 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 converting from syntax to symbol datum to string ... and all the way -back.

Finally, here’s a variation that accepts any number of name parts that -are joined with hyphens:

> (require (for-syntax racket/string racket/syntax))
> (define-syntax (hyphen-define* stx)
    (syntax-case stx ()
      [(_ (names ...) (args ...) body0 body ...)
       (let* ([names/sym (map syntax-e (syntax->list #'(names ...)))]
              [names/str (map symbol->string names/sym)]
              [name/str (string-join names/str "-")]
              [name/sym (string->symbol name/str)])
         (with-syntax ([name (datum->syntax stx name/sym)])
           #`(define (name args ...)
               body0 body ...)))]))
> (hyphen-define* (foo bar baz) (v) (* 2 v))
> (foo-bar-baz 50)

100

To review:

  • You can’t use a pattern variable outside of a template. But +back.

    4.1.3 Another example

    Finally, here’s a variation that accepts an arbitary number of name +parts to be joined with hyphens:

    > (require (for-syntax racket/string racket/syntax))
    > (define-syntax (hyphen-define* stx)
        (syntax-case stx ()
          [(_ (names ...) (args ...) body0 body ...)
           (let* ([names/sym (map syntax-e (syntax->list #'(names ...)))]
                  [names/str (map symbol->string names/sym)]
                  [name/str (string-join names/str "-")]
                  [name/sym (string->symbol name/str)])
             (with-syntax ([name (datum->syntax stx name/sym)])
               #`(define (name args ...)
                   body0 body ...)))]))
    > (hyphen-define* (foo bar baz) (v) (* 2 v))
    > (foo-bar-baz 50)

    100

    To review:

    • You can’t use a pattern variable outside of a template. But you can use syntax or # on a pattern variable to make an ad hoc, "fun size" template.

    • If you want to munge pattern variables for use in the template, with-syntax is your friend, because it lets you @@ -343,7 +343,8 @@ early draft and encouraged me to keep going. Sam Tobin-Hochstadt and Robby Findler also encouraged me. Matthew Flatt showed me how to make a Scribble interaction print syntax as "syntax" rather than as "#'". Jay McCarthy helped me -catch some mistakes and confusions.

      Finally, I noticed something strange. After writing much of this, when +catch some mistakes and confusions. Jon Rafkind pointed out some +problems. Kieron Hardy reported a font issue and some typos.

      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 a9bf5c3..927ae48 100644 --- a/main.rkt +++ b/main.rkt @@ -339,10 +339,10 @@ Using @racket[cdr] slices off the first item of the list, @racket[reverse] changes it to @racket[(values "i" "am" "backwards")]: @i[ -(reverse (cdr '("backwards" "am" "i" values))) +(reverse (cdr '(reverse-me "backwards" "am" "i" values))) ] -Finally we use @racket[syntax->datum] to convert this back to +Finally we use @racket[datum->syntax] to convert this back to @racket[syntax]: @i[ @@ -1547,7 +1547,8 @@ early draft and encouraged me to keep going. Sam Tobin-Hochstadt and 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["#'"]. Jay McCarthy helped me -catch some mistakes and confusions. +catch some mistakes and confusions. Jon Rafkind pointed out some +problems. Kieron Hardy reported a font issue and some typos. Finally, I noticed something strange. After writing much of this, when I returned to some parts of the Racket documentation, I noticed it had