The Racket Guide阅读笔记,chapter 16, 17.

16 Macros

本文描述Racket的宏。

另一个值得参考的是Fear of Macros

Racket提供有宏调试器,以及syntax/parse库来帮助宏的写作。

16.1 Pattern-Based Macros

16.1.1 define-syntax-rule

(define-syntax-rule pattern template)

16.1.2 Lexical Scope

宏具有文法作用域。

16.1.3 define-syntax and syntax-rules

当然,也支持从scheme中继承而来的

(define-syntax id
  (syntax-rules (literal-id ...)
    [pattern template]
    ...))

16.1.4 Matching Sequences

输入rotate的例子:

(define-syntax rotate
  (syntax-rules ()
    [(rotate a c ...)
     (shift-to (c ... a) (a c ...))]))
 
(define-syntax shift-to
  (syntax-rules ()
    [(shift-to (from0 from ...) (to0 to ...))
     (let ([tmp from0])
       (set! to from) ...
       (set! to0 tmp))]))

16.1.5 Identifier Macros

前面定义的swap以及rotate,必须在括号里面使用。为了避免这个问题,需要使用标识符宏(identifier macro)。

(define-syntax val
  (lambda (stx)
    (syntax-case stx ()
      [val (identifier? (syntax val)) (syntax (get-val))])))

(define-values (get-val put-val!)
  (let ([private-val 0])
    (values (lambda () private-val)
              (lambda (v) (set! private-val v)))))

下面的执行变成可能:

> val
0
> (+ val 3)
3
> (val)
. val: bad syntax in: (val)

16.1.6 set! Transformers

可以直接用make-set!-transformer来创建可更改的值。

16.1.7 Macro-Generating Macros

可以写一个宏(define-get/put-id val get-val put-val!)来自动生成上面例子中所需的宏。

16.1.8 Extended Example: Call-by-Reference Functions

讲述使用define-cbr来定义一个call-by-reference函数。

16.2 General Macro Transformers

16.3 Module Instantiations and Visits

17 Creating Languages

上一章中的宏有以下限制:

  • 宏无法在其上下文之内对语法进行限制,无法改变环绕其的形式
  • 宏只会在文本意义上对语言进行扩展,最终还是要使用到语言的标识符,关键字以及字面值。

也就是说宏只在expander层面对语言进行扩展,却无法在reader层面对语言进行扩展。

17.1 Module Languages

模块的初始导入可以被称为模块语言。常见的如racekt以及racket/base。但是你可以自定义模块语言。通过使用all-from-out,except-out以及rename-out,可以修改既有的racekt从而产生racket的一个变体。

17.1.1 Implicit Form Bindings

有一些隐含模式必须包含在模块之内,比如#%app,#%datum,#%top等

> (module just-lambda racket
    (provide lambda #%module-begin
             ; ten needs these, too:
             #%app #%datum))
> (module ten 'just-lambda
    ((lambda (x) x) 10))
> (require 'ten)

17.1.2 Using #lang s-exp

#lang层面实现一个语言会更复杂一些。

#lang s-exp module-name
form ...

17.2 Reader Extensions

使用#reader形式可以在reader层级修改语言。

#reader ‹module-path› ‹reader-specific›

定义了下面的之后

; five.rkt
#lang racket/base
 
(provide read read-syntax)
 
(define (read in) (list (read-string 5 in)))
(define (read-syntax src in) (list (read-string 5 in)))

'(1 #reader"five.rkt"234567 8)就变成了'(1 ("23456") 7 8)

'(1 #reader"five.rkt" 234567 8)就变成了'(1 (" 2345") 67 8)

17.2.1 Source Locations

17.2.2 Readtables

17.3 Defining new #lang Languages

如何定义一个#lang language

language决定了剩下部分如何在reader层级解析。

其他略。

(本篇完)