收集一些跟Racket中Macro相关额文章。
很多编程语言中都提供宏功能,比如C语言中的预处理器所提供的就是宏功能,主要用于代码的替换和生成。
但Racket中提供的宏功能不止步于代码的替换和生成,更像是完整的编译期间的编程语言,具有文法作用域,更可以指定语法类型,定义语法参数等等,而且可以递归嵌套,相当之复杂。
参考文档
- Racket Guide: Macro
- Racket Reference: Macro
- Syntax/parse
- Programming Languages介绍Racket为什么可以算作language oriented的language
- Fear of Macro
- Coursera Programming Languages
- Macros and Languages in Racket
- Macros in Racket: an Outline
- Learning Racket #2: Macros, Macros and a Bit of Modules
- bennn / syntax-parse-example
- Uppsala UniversityInformation TechnologyAFP - Advanced Functional Programming (2013) - MainLectures & Slides
- Macros that Work Together
- From Macros to DSLs: The Evolution of 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)
Beautiful 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)
之内。
(更新完)