Scheme 的语法定义方式

By guenchi at 2018-02-06 • 0人收藏 • 311人看过

Syntax Definitions

这里讨论几种 Scheme 可以使用的语法定义方式。

syntax-rules()

这是 Revised 5 Report on the Algorithmic Language Scheme (R5RS) 里的一种“干净”(hygienic) 的宏定义方式。这种方式的思 想请参考 Alan Bawden and Jonathan Rees. "Syntactic closures". Proceedings of the 1988 ACM Conference on Lisp and Functional Programming. July 1988.

我们的 coroutine 宏如果用 syntax-rules 定义,看起来要舒服一 些,因为它可以使用一种很直观的“省略号”方式表示可变的参数。

(define-syntax coroutine
  (syntax-rules ()
    ((coroutine arg resume body ...)
     (letrec ((local-control-state
               (lambda (arg) body ...))
              (resume
               (lambda (c v)
                 (call/cc
                  (lambda (k)
                    (set! local-control-state k)
                    (c v))))))
       (lambda (v)
         (local-control-state v))))))

我们看到在这个 coroutine 的定义里,我们故意把 resume 放到了 coroutine 宏的参数列表中。以后调用这个宏,我们必须使用一个 符号作为第二个参数。就像这样:

(letrec ((timer (coroutine dummy resume
                           (let loop ((tick 0))
                             (resume controller tick)
                             (loop (+ tick 1)))))

         (controller (coroutine c resume
                                (let loop ((count 0))
                                  (if (< count c)
                                      (begin
                                        (display (resume timer 'go))
                                        (loop (+ 1 count))))))))
  (controller 6)
  )

这样我们实际上实在把一个名字 resume 交给 coroutine 宏,让它 把这个名字绑定到一个函数,然后我们调用这个函数,所以调用时, 我们可以随意换名字。比如我们用 go-on 也是一样的效果:

(letrec ((timer (coroutine dummy go-on
                           (let loop ((tick 0))
                             (go-on controller tick)
                             (loop (+ tick 1)))))

         (controller (coroutine c go-on
                                (let loop ((count 0))
                                  (if (< count c)
                                      (begin
                                        (display (go-on timer 'go))
                                        (loop (+ 1 count))))))))
  (controller 6)
  )

syntax-case

这种结构是由 Dybvig, Hieb, 和 Bruggeman 提出的,请参考 "Syntactic abstraction in Scheme", Lisp and Symbolic Computation, December 1993.

它把 syntax 作为一个语法结构,就像一个函数那样。在展开的时候, 它可以利用展开时刻的环境,对某些符号进行绑定和处理。

这样,我们的 coroutine 宏就可以利用 syntax-case 的威力得到一 个简洁的定义了:

(define-syntax (coroutine stx)
  (syntax-case stx ()
    [(src-coroutine arg body ...)
     (with-syntax ((resume (datum->syntax-object 
                          (syntax src-coroutine) 'resume)))
       (syntax (letrec
                   ((local-control-state
                     (lambda (arg) body ...))
                    (resume
                     (lambda (c v)
                       (call/cc
                        (lambda (k)
                          (set! local-control-state k)
                          (c v))))))
                 (lambda (v)
                   (local-control-state v)))))]))

这样调用:

(letrec ((timer (coroutine dummy
                            (let loop ((tick 0))
                              (begin 
                                (if (> tick 5)
                                    (set! tick 5))
                                (resume controller tick))
                              (loop (+ tick 1)))))

         (controller (coroutine c
                                (let loop ((count 0))
                                  (if (< count c)
                                      (begin
                                        (display (resume timer 'go))
                                        (loop (+ 1 count))))))))
  (controller 10)
)

就能得到正确结果。

调用时,你不必为 "resume" 指定一个名字。syntax-case 的分析请 见 SyntaxCaseExamples.

这种方式必须显示绑定一个宏里的符号,其它符号仍然是“干净的” 闭包起来了的。由于 syntax-case 可以在宏的输入符号和输出之间 架起桥梁,而且是“干净”的,也可以“不干净”。所以用法非常灵 活。很多 Scheme 解释器都支持这个方式。如果不支持,可以到

ftp://ftp.cs.indiana.edu/pub/scheme-repository/code

下载一个可移植的 syntax-case 实现。

defmacro

这种方式就是字面上的替换,跟 TeX, cpp 差不多。这种方式对于简 单的宏是很有用的特别是那种我们在 CoroutineProblem 里提到的 coroutine 宏的定义中,我们需要让展开时的输入捕获宏定义时的符 号绑定。

(require (lib "defmacro.ss"))
(define-macro coroutine
  (lambda (x . body)
    `(letrec ((+local-control-state
               (lambda (,x) ,@body))
              (resume
               (lambda (c v)
                 (call/cc
                  (lambda (k)
                    (set! +local-control-state k)
                    (c v))))))
       (lambda (v)
         (+local-control-state v)))))

调用方法跟 syntax-case 的一样。

可是这种方式不小心就会导致名字冲突,而且替换以后的符号不一定 附和语法规则。最要命的弱点就是替换后的文本和源程序没有任何关 联,调试程序的时候看到一大堆展开的代码不知道自己到底怎么写的 了。


登录后方可回帖

登 录
信息栏

Scheme中文社区

推荐实现 ChezScheme / r6rs / r7rs large
theschemer.org
Q群: 724577239

精华导览

社区项目

包管理器:Raven
HTTP服务器:Igropyr (希腊火)
官方插件:vscode-chez

社区目标:

完善足以使Scheme工程化和商业化的库,特别是开发极致速度的Web服务器和ANN模块。

一直以来Scheme缺少一个活跃的中文社区,同时中文资料的稀少,导致大多数因为黑客与画家和SICP而接触Scheme的朋友,在学完SICP后无事可做,不能将Scheme转换为实际的生产力。最后渐渐的放弃。
同时Chicken等实现,却因效率问题无法与其他语言竞争。本社区只有一个目的,传播Scheme的文明之火,在最快的编译器实现上,集众人之力发展出足够与其他语言竞争的社区和库。


友情链接:

Clojure 中文论坛
函数式·China


Loading...