From 0de939e5c66f49ddd57cfd2af92e6c7f39f73f3b Mon Sep 17 00:00:00 2001 From: Greg Hendershott Date: Fri, 2 Nov 2012 11:52:50 -0400 Subject: [PATCH] Break up very long subsection with some subsubsections. --- index.html | 8 ++++---- main.rkt | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index f657df7..dac6d59 100644 --- a/index.html +++ b/index.html @@ -1,6 +1,6 @@ -Fear of Macros

Fear of Macros

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

Fear of Macros

+
Copyright (c) 2012 by Greg Hendershott. All rights reserved.
Last updated 2012-11-02 11:51:39
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 @@ -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—first the name, then the value. Also it’s more 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.

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