球拍 - 在宏中遇到变量问题

时间:2014-07-21 11:14:21

标签: macros scheme racket

我目前正在尝试将变量分配给宏来存储内容:

(begin-for-syntax
  (define a 0))
(define-syntax (foo stx)
  (set! a (+ a 1))
  (datum->syntax stx a))
(foo)
(foo)
(foo)

编译完这段代码后,repl显示" 1 2 3"。但是,当我进入"(foo)"在repl中,下一个数字是" 1"而不是" 4"这是我的预期。

1
2
3
> (foo)
1

它看起来像变量" a"编译完成后重置。当我需要"同样的事情发生了。这个代码在另一个模块中。

有可能解决这个问题吗?

2 个答案:

答案 0 :(得分:2)

我无法解释为什么它不起作用,但我觉得在相位级别1中“隐藏”变量可能不是正确的方法。你可以通过模块和没有宏来实现相同的目标:

(module adder racket/base
  (provide foo)
  (define a 0)
  (define (foo)
    (set! a (add1 a))
    a))

(require 'adder)
(foo)
(foo)
(foo)

打印

1
2
3

然后序列在REPL级别继续:

> (foo)
4
> (foo)
5

当然你也可以使用一个简单的闭包:

(define foo
  (let ((a 1))
    (lambda ()
      (begin0
        a
        (set! a (add1 a))))))

答案 1 :(得分:1)

Racket具有separate compilation guarantee,这意味着单独模块的编译行为一致,因为突变和效果被重置,就像它们被单独编译一样,即使在一起编译时也是如此。这里REPL的行为方式相同。

解决这种特殊情况的一种方法是将宏扩展为执行突变的begin-for-syntax,而不是尝试在宏变换器中执行此操作:

#lang racket
(begin-for-syntax
  (define a 0))
(define-syntax (get-a stx)
  (datum->syntax stx a))
(define-syntax (foo stx)
  #'(begin
      (begin-for-syntax
        (set! a (+ a 1)))
      (get-a)))
(foo)
(foo)
(foo)

在REPL中输入(foo)会返回4

但是对于其他模块,这是不允许的,因为set!不能改变来自单独模块的变量。