【问题标题】:Chez Scheme: macro import at top levelChez Scheme:顶层的宏导入
【发布时间】:2019-01-13 22:57:52
【问题描述】:

我正在运行 Chez Scheme 9.5 并尝试在 图书馆。这是一个例子:

(library (forlib)
  (export for)
  (import (rnrs (6)))

  (define-syntax for
    (syntax-rules (in)
      [(for x in lst body1 body2 ...)
       (for-each (lambda (x) body1 body2 ...) lst)])))

我将它保存在文件 forlib.ss 中并从同一目录运行 chez。 然后在 REPL 中,我得到了这个:

> (import (forlib))
> (for x in '(1 2 3) (display x))
Exception: invalid syntax (for x in (quote (1 2 3)) (display x))
Type (debug) to enter the debugger.

如果我将语法定义更改为

(define-syntax for
  (syntax-rules ()
    [(for x lst body1 body2 ...)
     (for-each (lambda (x) body1 body2 ...) lst)])))

(没有in 关键字),一切正常:

> (import (forlib))
> (for x '(1 2 3) (display x))
123
> _

回到旧定义 in 关键字。 如果我把测试代码放到一个测试文件中:

;;; test-for.ss
(import (rnrs (6))
        (forlib))

(for x in '(1 2 3) (display x))

并尝试执行该文件,结果取决于我如何执行该文件。 如果我使用chez --program 运行这个程序,它会按预期工作:

$ chez --program test-for.ss
123
$ _

如果我使用chez --script 运行它,我会得到与上面相同的错误:

$ chez --script test-for.ss
Exception: invalid syntax (for x in (quote (1 2 3)) (display x)) at line 6, char 1 of test-for.ss
$ _

这提出了两个问题:

  • 为什么 REPL 和 --script 在导入语法形式时没有问题 特殊关键字,但拒绝接受具有特殊的语法形式 关键字?
  • --script--program 之间到底有什么区别?这 用户手册说--program表示文件内容被解释 作为一个 rnrs 顶级程序,但对它的语义保持沉默 --script 是。

最后,如果我输入上面的语法定义,请完成我的困惑 直接在 REPL 中,然后一切都按预期工作:

> (define-syntax for
    (syntax-rules (in)
      [(for x in lst body1 body2 ...)
       (for-each (lambda (x) body1 body2 ...) lst)])))
> (for x in '(1 2 3) (display x))
123
> _

那么从 直接在 REPL 中定义的库和语法转换器?

【问题讨论】:

  • forlib 需要导出 'in' 和 'for'
  • @ChrisVine:这只是解决方案的一部分。如果我按照您的建议尝试导出in,我会收到错误消息,因为forlib 中没有定义要导出的名称。正如下面@gmw 所指出的,要使其工作,forlib 必须创建一个可以导出的虚拟名称in
  • 在检查这一点时,你是对的。看到这个:cisco.github.io/ChezScheme/csug9.5/use.html#./use:s14。我很幸运在我的代码中使用了辅助关键字,因为它们已经由 chezscheme 定义,并且我正在宏中重新绑定它们。在这种情况下,我需要做的就是导出它们。

标签: scheme r6rs chez-scheme


【解决方案1】:

在 Chez Scheme 中,操作模式之间的一些语义不同。我将把这些模式命名为 r6rs 和 repl。

R6RS:

  • 程序(命令行上的--program)

REPL:

  • 脚本(命令行上的--script)
  • 文件(使用加载过程或作为不带 --program 或 --script 的 CLI 参数)
  • 当然还有 repl

对于您的特定问题,如果您希望 repl 和脚本与库和程序的行为相匹配,则需要从 forlib 定义并导出一个名为 in 的标识符。这可以像空定义一样简单,或者您可以创建一个在宏体外部使用时引发错误的语法定义。 简单:

(library (forlib)
  (export for in)
  (import (rnrs))

  (define in)

  (define-syntax for ...)
)

句法定义:

(library (forlib)
  (export for in)
  (import (rnrs))

  (define-syntax in
    (identifier-syntax
      (error #f "Misplaced aux syntax in")))

  (define-syntax for ...)
)

两者都可以正常工作;使用任何你喜欢的。

repl 和 r6rs 模式之间的其他区别——据我所知——源于它们如何评估表达式。在 repl 模式下,每个表达式都单独处理。也就是说,Chez 读取一个表达式,评估该表达式,并可能打印结果。在 r6rs 模式下,文件的全部内容作为一个单元进行评估。明确地说,repl 模式的顶级延续是“读取、评估、可能打印和循环”,而 r6rs 模式的顶级延续是下一个表达式。 例如,此代码在作为程序运行或作为脚本运行时的行为会有所不同:

(import (chezscheme))

(define println)

(printf "~x\n"
  (call/cc (lambda (k)
             (set! println k)
             1)))

(println 5)
(println 6)

另一个区别是,在 r6rs 模式下,您不能定义一个函数,然后在语法引用之外的语法大小写宏扩展中使用它。

(define ($two) 2)

(define-syntax two
  (lambda (x)
    (syntax-case x ()
      [_ ($two)])))

【讨论】:

  • 感谢您的出色回答。这是我在 Chez 中使用这种类型的代码所需要知道的全部内容。由于与 REPL 交互的一对一性质,我也很欣赏 REPL 模式和 R6RS 模式之间的一些差异。不过,为什么宏导入在两种模式下表现不同,对我来说仍然是个谜。
猜你喜欢
  • 2019-09-18
  • 2020-04-22
  • 2021-10-17
  • 2016-10-17
  • 2020-08-17
  • 1970-01-01
  • 2021-03-16
  • 2016-10-20
  • 2016-07-25
相关资源
最近更新 更多