收集一些跟Racket中Macro相关额文章。

很多编程语言中都提供宏功能,比如C语言中的预处理器所提供的就是宏功能,主要用于代码的替换和生成。

但Racket中提供的宏功能不止步于代码的替换和生成,更像是完整的编译期间的编程语言,具有文法作用域,更可以指定语法类型,定义语法参数等等,而且可以递归嵌套,相当之复杂。

参考文档

Gotchas

Why doesn’t ‘define’ work in a macro?

如何在宏内定义一个外部可以用的标识符:

#lang racket

(require syntax/parse/define)

(define-syntax-parser make-a
  [(_)
   #:with a (datum->syntax this-syntax 'a)
   #'(define a 1)])

(make-a)
(display a)

Beau­tiful Racket / explainers / Hygiene也讲到如何破坏Hygiene。

(未完待续)

2021-05-08更新Gotchas

provide: not at module level

下面的代码是Racket School 2019的一道练习题

#lang racket

(require syntax/parse/define)
(require (for-syntax syntax/parse))

(define-syntax-parse-rule  (excercise1 fn)
  (begin
    (define (fn x)
      (number->string (add1 x)))
    (provide
     (contract-out
      [fn
       (-> number? string?)]))))

如果在DrRacket的definition windows执行(excercise1 bigger-string)没有任何问题。但是在DrRacket的interactive window执行就会出现:

provide: not at module level in: (provide (contract-out (bigger-string1 (-> number? string?))))

一开始摸不着头脑,后来查文档得知:

  • provide确实只能出现在module level
  • begin在不同的处境有不同的涵义
    • 在顶级或者部级或者局部定义上下文中,则有(begin form ...),其内容序列会拼接到外围环境
    • 在表达式上下文中,则有(begin expr ...+)上下文,其内容序列被当作表达式执行,所以就出现了上述的错误

struct: the first argument to the #:methods specification is not a name for a generic interface

#lang racket

(require racket/generic)

(begin-for-syntax
  
  (define-generics column-utils
    [gen-column-exists column-utils column-name])

  (struct column-spec
    (name type constraints)
    #:transparent
    #:methods gen:column-utils
    [(define (gen-column-exists self column-name)
       (displayln "gen-column-exits"))]))

上面的代码会出现错误:

struct: the first argument to the #:methods specification is not a name for a generic interface in: gen:column-utils

只是因为(require racket/generic)没有在(begin-for-syntax)之内。

(更新完)