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?

Leif Andersen
  • 21,580
  • 20
  • 67
  • 100

3 Answers3

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!
Leif Andersen
  • 21,580
  • 20
  • 67
  • 100
Asumu Takikawa
  • 8,447
  • 1
  • 28
  • 43
  • 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. – Leif Andersen Aug 18 '15 at 14:07
  • 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.) – Leif Andersen Aug 18 '15 at 14:08
  • Hmm...actually, never mind. When I try it on the latest version of racket it works like you describe it. Thanks. – Leif Andersen Aug 18 '15 at 14:35
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
stchang
  • 2,555
  • 15
  • 17
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

soegaard
  • 30,661
  • 4
  • 57
  • 106