Difference between `eval` and `eval-syntax`
Asked Answered
P

3

9

According to the documentation eval and eval-syntax behave the same with the exception that 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.

I am having a hard time understanding what this means. I get the impression that involves namespaces somehow, but I can't come up with an example program where eval and eval-syntax behave differently. (When given a syntax object.)

So how do eval and eval-syntax differ, or at least can you give me a sample program that shows them behaving differently?

Profluent answered 17/8, 2015 at 23:23 Comment(0)
S
9

Here is a sample interaction that shows them behaving differently:

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]

This shows that namespace-syntax-introduce is applied to the syntax object stx in the eval case using a namespace that has the vector bindings, which is why the vector-map application succeeds.

In the eval-syntax case, the syntax object does not have the lexical information for vector-map and no namespace introduction is done, so it results in an error.

Note that you need the module to show this difference rather than a top-level definition of the syntax object because top-level bindings are special. See this bit from namespace-syntax-introduce:

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

You can get similar behavior inside of a module:

#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!
Sharpfreeze answered 18/8, 2015 at 8:3 Comment(3)
Hmm...so well these do behave differently. Although it seems to have the exact same behavior if you remove the first two expressions, and use (eval stx) and (eval-sytnax stx) without the ns argument.Profluent
Also, if I do the whole thing in a module, it seems like eval and eval-syntax do the same thing again. (error that vector-map is undefined.)Profluent
Hmm...actually, never mind. When I try it on the latest version of racket it works like you describe it. Thanks.Profluent
W
4

Here's the dual to the program at the bottom of Asumu's answer:

#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
Wifeless answered 18/8, 2015 at 15:9 Comment(0)
Z
2

The keyword here is "enrichen". The docs says that namespace-syntax-introduce is used by eval to enrichen the syntax-object:

(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). 

This implies that an example is given by a syntax-object stx that refers to a binding in the current namespace where eval is called that weren't available where the syntax-object was constructed. And that's exactly what Asumu's example does.

FWIW here is my understanding of how "enrichen-top-level-form" works:

(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))]))

See more here: https://github.com/soegaard/meta/blob/master/expander/expander.rkt#L348

Zaratite answered 18/8, 2015 at 13:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.