6
votes

This seems like a simple question; perhaps it is so simple that it is difficult to find a search that will find the answer. In Scheme (specifically, the Guile implementation if that makes any difference) how do I evaluate something that has been quoted?

Here's what I'm trying to do.

I basically need to ensure that a function I define gets its arguments evaluated in a specific order, because side effects caused by evaluating one argument are depended on during the evaluation of other arguments. However, Scheme says arguments can be evaluated in any order, so I want to manually force it by quoting the arguments and then manually evaluating them in the order that is needed.

It appears that "eval" is supposed to do what I want, but it has two problems:

  1. Its use is discouraged, so I feel like there should be a better way to accomplish what I want to do here.
  2. In Scheme it appears that eval takes a second parameter which is the environment. This is confusing to me. I want it to eval in the same environment the statement appears in, so why I should need a second parameter? Is this even possible? I've played with eval a bit and it appears that some implementations requires different parameters (e.g. mit-scheme doesn't even know what (interaction-environment) is!!!)

I've tried other tricks, like building up a lambda:

(list 'lambda '() '(car (b c)))

but it appears that this would then have to be evaluated to generate a procedure. I also tried:

(list lambda '() '(car (b c)))

but this returns a "primitive-builtin-macro" which doesn't work either.

Edit: Looks like a macro will work for controlling order of evaluation: (defmacro test1 (a b) `(begin ,b ,a))

3

3 Answers

8
votes

eval is utterly the wrong tool for just changing the order of evaluation of arguments. Create a macro instead:

;; (my-fun e1 e2)
;; Just calls my-real-fun, but evaluates e2 before e1
(define-syntax my-fun
  (syntax-rules ()
    [(my-fun e1 e2)
     ;; let* has guaranteed order of evaluation
     (let* ([y e2]
            [x e1])
       (my-real-fun x y))]))

(define (my-real-fun x y) ....)

Or use defmacro, if you must.

3
votes

If you need to evaluate a list structure (nested lists with quoted symbols that represent a Scheme program text), then you should use eval. Scheme requires passing an environment as a second argument, even if it is the current environment:

(eval '(+ x y) (interaction-environment))

If you just need to do your calculations in a particular order, you can enforce the order of evaluation for side effects by using begin, let, or just a function body. They define a sequence of evaluations:

(let ((x 42))
  ; eval with effects #1
  (display x)
  ; eval with effects #2
  (display (+ x 1)))

Edit: If you need to have a parametrized block of code where you can pass expressions unevaluated and then force their evaluation in some particular order, then you may use one of these techniques:

  • A macro (as you've mentioned already, just for completeness):

    > (defmacro test1 (a b) `(begin ,b ,a))
    > (test1 (display 2) (display 3)
    32
    
  • A delayed computation (Scheme's special syntax for lazy evaluation):

    > (define (test1 a b) (begin (force b) (force a)))
    > (test1 (delay (display 2)) (delay (display 3)))
    32
    
  • A regular lambda abstraction and application

    > (define (test1 a b) (begin (b) (a)))
    > (test1 (lambda () (display 2)) (lambda () (display 3)))
    32
    
0
votes

You were on the right track with passing in lambdas. If you have

(define (f x y z) ...)

... then you can call it like so:

(f
  (lambda () a)
  (lambda () b)
  (lambda () c))

This will call f with all arguments (a, b, c) in an unevaluated form. Inside f, you have complete power to choose the order you evaluate them. The only difference is that you have to explicitly call (x), (y) and (z), and capture their values inside define or let-like statements. This lets you ensure that the side effects happen only once.

No need for macros here at all. Btw, don't worry about using lots of lambdas everywhere, they are very cheap.