This question is similar in scope to: In R6RS Scheme, is there a way to get the current environment for use with eval? but I'd like to take it a step further and ask how you'd remedy something like this.
My issue is further confounded in that in my case '(+ x y)
is an arbitrary unevaluated lambda statement. Unevaluated because it may contain calls to variables that are part of the let (and since Scheme doesn't have faith that the procedure will be called in an environment containing those variables when the current one does not, it fires off an undefined identifier error). So the question becomes: how can I restructure my code, such that this scoping nightmare is no longer an issue? I'd like to still be able to use the environment variables from the let whenever the lambda is called.
I'm using Pretty Big
The intent is to create classes in Scheme. My approach so far is pretty big (no pun intended) but looks like:
(define (dispatch msg methods args)
(if (null? methods) (display "Method signature not found.")
(let (
(m-name (caar methods))
(m-args (cadar methods))
(m-body (caddar methods)))
(if (and (eq? msg (caar methods)) (eq? (length args) (length (cadar methods))))
`(lambda ,m-args ,m-body)
(dispatch msg (cdr methods) args)))))
(define (build-lets c-def)
(let (
(i-vars (cadr c-def))
(meths (caddr c-def)))
(eval `(append ',i-vars (list (list 'methods '',meths))))))
(define (new c-def . args)
(apply (eval `(lambda ,(map cadr (cadr c-def))
(let* ,(build-lets c-def)
(lambda (msg . args)
(letrec ((meth (dispatch msg methods args)))
(apply meth args))))))
args))
Where c-def is a class def of the form (say for a point)
'(();Name of parent
((yvalue y) (xvalue x)) ;Instance variables: (i-var constructor-arg)
((getx () xvalue) ;Methods, ((name args body) ...)
(setx (x) (set! xvalue x)))))
If you can make
let
part of the eval then it will work. Below is an example:Something similar (but simpler) to your code which doesn't work:
Making let part of eval make it works:
This doesn't implement all the syntax you have in mind, but it might illustrate the techniques needed to implement it cleanly.
The trick is to write a macro that makes a closure. The instance variables go in a lexical scope (the
let
) that holds the closure. The closure is the dispatcher. The methods are lambdas defined within the dispatcher, so the instance variables are within the same scope as the methods.