Reputation: 63
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?
Upvotes: 0
Views: 272
Reputation: 48775
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.
Upvotes: 1
Reputation: 236150
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
Upvotes: 1