Unable to use eval on user input in Racket

2019-06-22 15:51发布

问题:

I'm currently learning Scheme (using Racket), but one of the challenges I'm coming upon is trying to execute the following bit of code, which is meant to execute Racket code from user input using eval:

(display (eval (read)))

 

Here's some of the weird behavior I've observed so far:

  1. (display (eval (read))) in the definition window prompts for keyboard input, as expected, when the definitions are run. However, providing the input
    ((lambda (x) (+ x 1)) 1)
     
    gives the error
    ?: function application is not allowed; no #%app syntax transformer is bound in: ((lambda (x) (+ x 1)) 1)

  2. On the other hand, using (display ((eval (read)) 1)) and providing input
    (lambda (x) (+ x 1))
     
    returns the error
    lambda: unbound identifier; also, no #%app syntax transformer is bound in: lambda

  3. However, running (display (eval (read))) and providing ((lambda (x) (+ x 1)) 1) in the console pane, as opposed to the definitions pane, prints out 2, as expected.

What is the reason for this behavior?

回答1:

It looks like you don't have the namespace set up. If you're running (eval (read)) within a file, it doesn't work because the current-namespace is set to an empty namespace by default. You can set up a namespace with racket/base in it by doing (current-namespace (make-base-namespace)) first:

#lang racket
(current-namespace (make-base-namespace))
(println (eval (read)))

Running this program and giving it the input ((lambda (x) (+ x 1)) 1) results in it printing 2.

The reason it worked in the interactions window (item 3 of your weird-behavior list), is that in the interactions window, the current-namespace parameter is set to the namespace of the file.

This is not true for the definitions window, the main program, so you have to set the current-namespace yourself, or pass a namespace in as a second argument to eval:

#lang racket
(define ns (make-base-namespace))
(println (eval (read) ns))


回答2:

Racket, the software package, has support for both R5RS and R6RS and will probably get support for R7RS. The software also has several non standard languages in its own Racket language family that has much in common with Scheme but is not Scheme. Alex has made an excellent answer for that language so I though I'd add information about Scheme since you write you're learning Scheme which is not the same as learning Racket when it comes some corner cases including eval.

eval is a procedure which has had breaking changes between the different Scheme reports since it became mandatory in R5RS. Here are some examples from different versions of the standard:

#!r6rs

(import (rnrs)
        (rnrs eval))

(display (eval '((lambda (x) (+ x 1)) 1) 
               (environment '(rnrs)))) 
; ==> undefined, prints 2

The slightly older but still in common used R5RS:

#!r5rs

(display (eval '((lambda (x) (+ x 1)) 1) 
               (scheme-report-environment 5))) 
; ==> undefined, prints 2

The to come R7RS which only has it's small version ratified so far:

#!r7rs

(import (scheme)
        (scheme eval))

(display (eval '((lambda (x) (+ x 1)) 1) 
               (environment '(scheme)))) 
; ==> undefined, prints 2