diff --git a/index.html b/index.html index 5f13d83..f657df7 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.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-02 11:33:23
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.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-02 11:42:41
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.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 @@ -115,15 +115,13 @@ syntax list. We didn’t take (name when we used pattern-matching. In general, a syntax transformer won’t care about that, because it is the name of the transformer binding. In other words, a macro usually doesn’t care -about its own name.

Instead of:

> (define-syntax (our-if-v2 stx)
    (define xs (syntax->list stx))
    (datum->syntax stx `(cond [,(cadr xs) ,(caddr xs)]
                              [else ,(cadddr xs)])))

We can write:

> (define-syntax (our-if-using-match stx)
    (match (syntax->list stx)
      [(list name condition true-expr false-expr)
       (datum->syntax stx `(cond [,condition ,true-expr]
                                 [else ,false-expr]))]))
> (our-if-using-match #t "true" "false")

match: undefined;

 cannot reference an identifier before its definition

  in module: 'program

  phase: 1

But wait, we can’t. It’s complaining that match isn’t -defined. We haven’t required the racket/match module?

It turns out we haven’t. Remember, this transformer function is -working at compile time, not run time. And at compile time, only -racket/base is required for you automatically. If we want -something like racket/match, we have to require it -ourselves—and require it for compile time. Instead of using -plain (require racket/match), the way to say this is to use -(require (for-syntax racket/match))the for-syntax -part meaning, "for compile time".

So let’s try that:

> (require (for-syntax racket/match))
> (define-syntax (our-if-using-match-v2 stx)
    (match (syntax->list stx)
      [(list _ condition true-expr false-expr)
       (datum->syntax stx `(cond [,condition ,true-expr]
                                 [else ,false-expr]))]))
> (our-if-using-match-v2 #t "true" "false")

"true"

Joy.

3.5 begin-for-syntax

We used for-syntax to require the +about its own name.

Instead of:

> (define-syntax (our-if-v2 stx)
    (define xs (syntax->list stx))
    (datum->syntax stx `(cond [,(cadr xs) ,(caddr xs)]
                              [else ,(cadddr xs)])))

We can write:

> (define-syntax (our-if-using-match stx)
    (match (syntax->list stx)
      [(list name condition true-expr false-expr)
       (datum->syntax stx `(cond [,condition ,true-expr]
                                 [else ,false-expr]))]))

Great. Now let’s try using it:

> (our-if-using-match #t "true" "false")

match: undefined;

 cannot reference an identifier before its definition

  in module: 'program

  phase: 1

Oops. It’s complaining that match isn’t defined.

Our transformer function is working at compile time, not run time. And +at compile time, only racket/base is required for you +automatically—not the full racket.

Anything beyond racket/base, we have to require +ourselves—and require it for compile time using the +for-syntax form of require.

In this case, instead of using plain (require racket/match), +we want (require (for-syntax racket/match))the +for-syntax part meaning, "for compile time".

So let’s try that:

> (require (for-syntax racket/match))
> (define-syntax (our-if-using-match-v2 stx)
    (match (syntax->list stx)
      [(list _ condition true-expr false-expr)
       (datum->syntax stx `(cond [,condition ,true-expr]
                                 [else ,false-expr]))]))
> (our-if-using-match-v2 #t "true" "false")

"true"

Joy.

3.5 begin-for-syntax

We used for-syntax to require the racket/match module because we needed to use match at compile time.

What if we wanted to define our own helper function to be used by a macro? One way to do that is put it in another module, and diff --git a/main.rkt b/main.rkt index 9f0d473..1163a15 100644 --- a/main.rkt +++ b/main.rkt @@ -496,21 +496,27 @@ We can write: (match (syntax->list stx) [(list name condition true-expr false-expr) (datum->syntax stx `(cond [,condition ,true-expr] - [else ,false-expr]))])) + [else ,false-expr]))]))] + +Great. Now let's try using it: + +@i[ (our-if-using-match #t "true" "false") ] -But wait, we can't. It's complaining that @racket[match] isn't -defined. We haven't required the @racket[racket/match] module? +Oops. It's complaining that @racket[match] isn't defined. -It turns out we haven't. Remember, this transformer function is -working at compile time, not run time. And at compile time, only -@racket[racket/base] is required for you automatically. If we want -something like @racket[racket/match], we have to require it -ourselves---and require it @italic{for compile time}. Instead of using -plain @racket[(require racket/match)], the way to say this is to use -@racket[(require (for-syntax racket/match))]---the @racket[for-syntax] -part meaning, "for compile time". +Our transformer function is working at compile time, not run time. And +at compile time, only @racket[racket/base] is required for you +automatically---not the full @racket[racket]. + +Anything beyond @racket[racket/base], we have to require +ourselves---and require it for compile time using the +@racket[for-syntax] form of @racket[require]. + +In this case, instead of using plain @racket[(require racket/match)], +we want @racket[(require (for-syntax racket/match))]---the +@racket[for-syntax] part meaning, "for compile time". So let's try that: