0
votes

I'm working on a metacircular evaluator in Scheme for homework, and I need to allow the user to install special forms by adding them to a table. The idea is that, when the user enters something like (square 5), the evaluator will look up forms named square. If it's found, it will return a lambda statement, something like (lambda (x) (* x x)).

I'm having a problem when the code returns the lambda statement. I get the following error message:

Error: Bad function object:(lambda (x) (* x x))

The really strange part is that I can pass parameters into functions retrieved from my table, it's just that I have to previously define the procedure body as a lambda statement, not as a list that starts with lambda

For reference, here's the code that doesn't work. exp will be something like (install-special-form 'square (lambda (x) (* x x))), so in this case, name evaluates to square and func evaluates to (lambda (x) (* x x)):

(define (install-eval exp)
  (define name (cadadr exp))
  (define func (caddr exp))
    (if (special-form-lookup (list name func))
        #f
       (begin
         (append! special-forms-table (list name func))
         name)))

And here's some code that does work:

(define (install exp-list)
  (append! special-forms-table exp-list))
(install (list 'square (lambda (x) (* x x))))

I'm guessing my problem is that, when using the code that doesn't work, lambda is evaluated as a quote, rather than an actual lambda? How can I get my user input to store an actual lambda statement that can be retrieved and used?

2

2 Answers

1
votes

You're probably storing the lambda as a list of symbols, not as an actual procedure. That's why this won't work:

(define f '(lambda (x) (* x x)))
(f 10)
=> Error: Bad function object: (lambda (x) (* x x))

Try evaluating it first:

((eval f) 10)
=> 100
1
votes

When you return the list (lambda (x) (* x x)) you cannot apply it with the host since it would be like doing ('(lambda (x) (* x x)) 5). Try it. you'll get the same error.

When Scheme evaluates the special form (lambda (x) (* x x)) it returns a closure object with the environment to when it was created. When you call it it will run the body with that environment with the added bingind to x. This needs to be simulated in your interpreter so usually (lamba (args ...) body) usually is evaluated to (closure-tag (args ...) environment body). Your apply needs these to be able to call eval on the body with that correct environment. Oscars suggestion to use eval won't work in the long run since you won't be able to make closures in the interpreter with eval and I would consider it cheating if you did get away with it.