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

Difference between `eval` and `eval-syntax`

根据文档,evaleval-syntax 行为相同,但 eval enriches the input syntax.

除外

If top-level-form is a syntax object whose datum is not a compiled form, then its lexical information is enriched before it is sent to the evaluation handler:

Like eval, except that stx must be a syntax object, and its lexical context is not enriched before it is passed to the evaluation handler.

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

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

这是一个示例交互,显示了它们的不同行为:

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-introduceeval 情况下使用具有矢量绑定的名称空间应用于语法对象 stx,这就是为什么 vector-map申请成功

eval-syntax的情况下,语法对象没有vector-map的词法信息,也没有做命名空间的引入,所以会报错。

请注意,您需要模块来显示这种差异,而不是语法对象的顶级定义,因为顶级绑定是特殊的。从 namespace-syntax-introduce:

中看到这一点

The additional context is overridden by any existing top-level bindings in the syntax object’s lexical information

您可以在模块内部获得类似的行为:

#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!

这里的关键词是"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 的示例所做的。

FWIW 这是我对 "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

这是 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