diff --git a/index.html b/index.html index ea88dcf..7fa77ba 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-02 14:00:28
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 +

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 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 @@ -61,7 +61,7 @@ string literal. How about returning (What’s the input?

Our examples so far have ignored the input syntax and output some fixed syntax. But typically we will want to transform in the input -syntax into somehing else.

Let’s start by looking closely at what the input actually is:

> (define-syntax (show-me stx)
    (print stx)
    #'(void))
> (show-me '(i am a list))

#<syntax:10:0 (show-me (quote (i am a list)))>

The (print stx) shows what our transformer is given: a syntax +syntax into somehing else.

Let’s start by looking closely at what the input actually is:

> (define-syntax (show-me stx)
    (print stx)
    #'(void))
> (show-me '(+ 1 2))

#<syntax:10:0 (show-me (quote (+ 1 2)))>

The (print stx) shows what our transformer is given: a syntax object.

A syntax object consists of several things. The first part is the S-expression representing the code, such as '(+ 1 2).

Racket syntax is also decorated with some interesting information such as the source file, line number, and column. Finally, it has @@ -168,8 +168,9 @@ transformer, and worked up from that, we won’t have that problem. We can appreciate define-syntax-rule as a convenient shorthand, but not be scared of, or confused about, that for which it’s shorthand.

Most of the materials I found for learning macros, including the -Racket Guide, do a very good job explaining how patterns -and templates work. I’m not going to regurgitate that here.

Sometimes, we need to go a step beyond the pattern and template. Let’s +Racket Guide, do a very good job explaining +how +patterns and templates work. So I won’t regurgitate that here.

Sometimes, we need to go a step beyond the pattern and template. Let’s look at some examples, how we can get confused, and how to get it working.

4.1 Pattern variable vs. template—fight!

Let’s say we want to define a function with a hyphenated name, a-b, but we supply the a and b parts separately. The Racket struct @@ -279,7 +280,7 @@ default. Only inside of our aif will it have a meani value:

> (require racket/stxparam)
> (define-syntax-parameter it
    (lambda (stx)
      (raise-syntax-error (syntax-e stx) "can only be used inside aif")))
> (define-syntax-rule (aif condition true-expr false-expr)
    (let ([tmp condition])
      (if tmp
          (syntax-parameterize ([it (make-rename-transformer #'tmp)])
            true-expr)
          false-expr)))
> (aif 10 (displayln it) (void))

10

> (aif #f (displayln it) (void))

Inside the syntax-parameterize, it acts as an alias for tmp. The alias behavior is created by make-rename-transformer.

If we try to use it outside of an aif form, and -it isn’t otherwise defined, we get an error like we want:

> (displayln it)

it: can only be used inside aif

But we can still define it as a normal variable:

> (define it 10)
> it

10

6 What’s the point of splicing-let?

I stared at racket/splicing for the longest time. What does +it isn’t otherwise defined, we get an error like we want:

> (displayln it)

it: can only be used inside aif

But we can still define it as a normal variable:

> (define it 10)
> it

10

For a deeper look, see Keeping it Clean with Syntax Parameters.

6 What’s the point of splicing-let?

I stared at racket/splicing for the longest time. What does it do? Why would I use it? Why is it in the Macros section of the reference?

Step one, cut a hole in the box de-mythologize it. For example, using splicing-let like this:

> (require racket/splicing)
> (splicing-let ([x 0])
    (define (get-x)
      x))
; get-x is visible out here:
> (get-x)

0

; but x is not:
> x

x: undefined;

 cannot reference an identifier before its definition

  in module: 'program

is equivalent to:

> (define get-y
    (let ([y 0])
      (lambda ()
        y)))
; get-y is visible out here:
> (get-y)

0

; but y is not:
> y

y: undefined;

 cannot reference an identifier before its definition

  in module: 'program

This is the classic Lisp/Scheme/Racket idiom sometimes called "let diff --git a/main.rkt b/main.rkt index f940316..a9bf5c3 100644 --- a/main.rkt +++ b/main.rkt @@ -242,7 +242,7 @@ Let's start by looking closely at what the input actually @italic{is}: (define-syntax (show-me stx) (print stx) #'(void)) -(show-me '(i am a list)) +(show-me '(+ 1 2)) ] The @racket[(print stx)] shows what our transformer is given: a syntax @@ -674,8 +674,9 @@ but not be scared of, or confused about, that for which it's shorthand. Most of the materials I found for learning macros, including the -Racket @italic{Guide}, do a very good job explaining how patterns -and templates work. I'm not going to regurgitate that here. +Racket @italic{Guide}, do a very good job explaining +@hyperlink["http://docs.racket-lang.org/guide/pattern-macros.html" "how +patterns and templates work"]. So I won't regurgitate that here. Sometimes, we need to go a step beyond the pattern and template. Let's look at some examples, how we can get confused, and how to get it @@ -890,8 +891,10 @@ Using @racket[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: +@subsubsection{Another example} + +Finally, here's a variation that accepts an arbitary number of name +parts to be joined with hyphens: @i[ (require (for-syntax racket/string racket/syntax)) @@ -909,6 +912,7 @@ are joined with hyphens: (foo-bar-baz 50) ] + To review: @itemize[ @@ -1301,6 +1305,8 @@ But we can still define @racket[it] as a normal variable: it ] +For a deeper look, see @hyperlink["http://www.schemeworkshop.org/2011/papers/Barzilay2011.pdf" "Keeping it Clean with Syntax Parameters"]. + @; ---------------------------------------------------------------------------- @; ----------------------------------------------------------------------------