使用(lambda args())可以阻止我绑定args中的内容

时间:2017-09-06 20:37:48

标签: lambda macros scheme

以下代码有效:

(define foo (lambda (x)
  (lambda (y) (+ x y))))

((foo 2) 5) ; => 7

但是如果我想编写一个为我定义lambda的宏,其中包含任意数量的参数,如下所示:

(define-syntax create-lambda
  (syntax-rules ()
    ((_ name args)
     (define name (lambda args
       (lambda (y) (+ x y)))))))

并像这样使用它:

;; create the lambda named "foo" with arguments "(x)"
(create-lambda foo (x))

((foo 2) 5) ; => Unbound variable: x

我收到一个未绑定的变量错误 我知道 x 未定义,因为它包含在我的宏中的 args 中。

但如果我显示我的宏的结果,我得到:

(define foo (lambda (x) (lambda (y) (+ x y))))

这对我来说非常有用。

为什么将lambda参数定义为单个变量 args 会阻止我绑定内部的内容?

1 个答案:

答案 0 :(得分:2)

Scheme中的宏是卫生的。因此,如果您传递宏x,则它与宏中的变量x 不同,因为它们来自不同的范围。一个有用的实际例子是swap!宏:

(define-syntax swap! 
  (syntax-rules ()
    ((_ from to) 
     (let ((tmp from))
       (set! from to)
       (set! to tmp)))))

(define tmp 10)
(define test 20)
(swap! tmp test)
(list tmp test) ; ==> (20 10)

宏扩展器的天真版本可能会泄漏使用变量tmp的宏并最终为(20 20),但(swap! tmp test)的扩展可能看起来像这样:

 (let ((tmp$1 tmp))
   (set! tmp test)
   (set! test tmp$1)))))

我只是将1美元附加到宏创建的符号上。它可能最终看起来一样,但对于Scheme实现,它们是不同的变量,就像它们有不同的名称一样。宏不应该依赖于没有得到冲突的符号,因为程序员倾向于重用相同的符号。你的代码看起来像:

(define name 
  (lambda (x)
    (lambda (y$1) (+ x$1 $1)))))))

显然x$1未绑定且不存在。无论如何,你宁愿对一个名称和正文都作为输入的宏感兴趣,比如:

(define-syntax lambda-arity1
  (syntax-rules ()
    ; one argument
    ((_ (arg) body ...) 
     (lambda (arg) body ...))
    ; two or more arguments
    ((_ (arg arg2 ...) body ...) 
     (lambda (arg) (lambda-arity1 (arg2 ...) body ...)))))


(lambda-arity1 (a b c d) (+ a b c d))
; == (lambda (a) (lambda (b) (lambda (c) (lambda (d) (+ a b c d)))))

由于名称也来自列表中的用户a,因此它与正文中的a相同,并且完美无缺。 (((lambda-arity1 (a b) (+ a b) 1) 2) ; ==> 3