`eval` 和 `eval-syntax` 的区别
Difference between `eval` and `eval-syntax`
根据文档,eval
和 eval-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 行为不同的示例程序。 (当给定语法对象时。)
那么 eval
和 eval-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-introduce
在 eval
情况下使用具有矢量绑定的名称空间应用于语法对象 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-introduce
被 eval
用来丰富语法对象:
(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
根据文档,eval
和 eval-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 行为不同的示例程序。 (当给定语法对象时。)
那么 eval
和 eval-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-introduce
在 eval
情况下使用具有矢量绑定的名称空间应用于语法对象 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-introduce
被 eval
用来丰富语法对象:
(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