`eval` 和 `eval-syntax` 的区别

9
根据文档,evaleval-syntax的行为相同,唯一的区别在于eval丰富输入语法

如果top-level-form是一个语法对象,其数据不是编译形式,则在发送到评估处理程序之前,其词法信息将被丰富:

与eval类似,但stx必须是语法对象,并且在传递给评估处理程序之前其词法上下文不会被丰富。

我很难理解这意味着什么。我有一种印象,它涉及命名空间,但我想不出一个示例程序,其中eval和eval-syntax的行为不同。(当给定一个语法对象时。)

那么,evaleval-syntax有何不同,或者至少您能否给我一个显示它们行为不同的示例程序?

3个回答

9
这是一个展示它们不同行为的样例交互:
Welcome to Racket v6.2.900.10.
-> (define ns (make-base-namespace))  ; set up namespace ns
-> (eval '(require racket/vector) ns) ; bring in vector-map into ns
-> (module a racket/base
     (define stx #'(vector-map + #(1 2) #(3 4))) ; no vector-map here
     (provide stx))
-> (require 'a)
-> (eval stx ns)
'#(4 6)
-> (eval-syntax stx ns)
; vector-map: undefined;
;  cannot reference undefined identifier
; [,bt for context]

这表明在使用具有向量绑定的命名空间时,将namespace-syntax-introduce应用于eval情况下的语法对象stx,这就是为什么vector-map应用成功的原因。
eval-syntax情况下,语法对象没有vector-map的词汇信息,也没有进行命名空间引入,因此会导致错误。
请注意,您需要使用模块来显示此差异,而不是语法对象的顶级定义,因为顶级绑定是特殊的。请参阅namespace-syntax-introduce中的以下部分:

该附加上下文被语法对象的词汇信息中的任何现有顶级绑定所覆盖。

您可以在模块内获得类似的行为:
#lang racket/base                     ; racket/base does not include racket/vector
(define ns (make-base-namespace))     ; Create Namespace
(eval #'(require racket/vector) ns)   ; Load racket/vector into ns
(define stx #'(vector-map + #(1 2) #(3 4)))
(eval stx ns)                         ; => '#(4 6)
(eval-syntax stx ns)                  ; => ERROR!

嗯...所以这些确实表现不同。虽然如果您删除前两个表达式,并且在不使用ns参数的情况下使用(eval stx)(eval-syntax stx),它似乎具有完全相同的行为。 - Leif Andersen
另外,如果我在一个模块中完成整个事情,似乎evaleval-syntax再次执行相同的操作。(错误:vector-map未定义。) - Leif Andersen
嗯...其实算了吧。当我在最新版本的Racket上尝试时,它就像你描述的那样工作。谢谢。 - Leif Andersen

4
这是与Asumu答案底部程序相对应的双重程序:
#lang racket/base
(require racket/vector) ; adds vector-map to lexical scope

; use vector-map from lexical scope
(eval-syntax #'(vector-map + #(1 2) #(3 4)))  ; => #(4 6)

; vector-map not in dynamic scope
; (make-base-namespace == racket/base)
(eval '(vector-map + #(1 2) #(3 4)) (make-base-namespace)) 
; => ERR: vector-map: undefined

2
关键词是“enrichen”。文档中指出,namespace-syntax-introduceeval用来丰富语法对象。
(namespace-syntax-introduce stx) → syntax?

Returns a syntax object like stx, except that the current namespace’s bindings 
are included in the syntax object’s lexical information (see Syntax Objects). 

这意味着语法对象 stx 给出了一个示例,它指向当前命名空间中绑定的一个值,在构造该语法对象时该值并不可用于调用 eval。这正是 Asumu 的示例所做的事情。
顺便说一下,“enrichen-top-level-form” 如何工作的理解如下:
(define (enrichen-top-level-form top-level-form)
  ; see docs for eval
  (define introduce namespace-syntax-introduce)
  (match top-level-form
    [(? syntax? s)
     (match (syntax-e s)
       [(? compiled-expression? c) c]
       [(cons (? sym-or-id? mod?) more)
        (define mod (introduce mod?))
        (if (bound-identifier=? mod #'module)
            (datum->syntax #f (cons mod more))
            (introduce s))]
       [_ (introduce s)])]
    [d (enrichen-top-level-form (datum->syntax #f d #f))]))

点击此处查看更多信息:https://github.com/soegaard/meta/blob/master/expander/expander.rkt#L348


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接