Разница между "eval" и " eval-синтаксис`
согласно документации eval
и eval-syntax
вести себя так же, за исключением того, что eval
обогащает синтаксис ввода.
если top-level-form является синтаксическим объектом, datum которого не является скомпилированной формой, то его лексическая информация обогащается перед отправкой обработчику оценки:
как eval, за исключением того, что stx должен быть объектом синтаксиса, и его лексический контекст не обогащается до его передачи обработчик оценки.
мне трудно понять, что это значит. У меня создается впечатление, что это как-то связано с пространствами имен, но я не могу придумать пример программы, где eval и eval-синтаксис ведут себя по-разному. (При задании объекта синтаксиса.)
так как eval
и eval-syntax
different, или, по крайней мере, вы можете дать мне пример программы, которая показывает, что они ведут себя по-разному?
3 ответов
вот пример взаимодействия, показывает, что они ведут себя по-разному:
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
применяется к объекту синтаксис stx
на eval
случай с использованием пространства имен, которое имеет векторные привязки, поэтому vector-map
приложения успешно.
на eval-syntax
case, объект синтаксиса не имеет лексической информации для 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!
вот двойник к программе в нижней части ответа 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
ключевое слово здесь "обогатит". Врачи говорят, что 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