[plt-scheme] Evaluation in local environment?
Alright, I don't know why that previous message was sent as HTML, but let's
try this again, without using Thunderbird.
I'm a student using scheme for a class (at Northern Arizona University), and
I've run into a roadblock with an assignment. The idea is to create a form of
objects with simple functions, by creating a local environment with "let" with
all the instance variables and declaring the object (a lambda function) within
that environment. The main trick is that, of course, I have to be able to read
in arbitrary class definitions, as definedI thought I had a very elegant and
simple solution, which declared the functions within a let at construction and
then called whichever function was passed. So, if you call (point1 'setx 3),
all point1 just calls (setx 3) in its local environment (well, it's slightly
more complicated to allow for overloading with different numbers of arguments)
- a simple eval call on the list of arguments.
This is where I ran into my problem - eval evaluates in the global
environment. I can't declare the functions in the global environment, because
I need to be able to create new instances of each "object" dynamically, and
the functions can't refer to global variables.
Is there any way to achieve the same effect (passed a symbol, look up the
symbol and return the function it is attached to) but referring to the
environment created by a let function?
As an example, this is what is constructed by the "new" function I originally
created, if I use the definition for a "point" object provided by the
professor:
(let ((x '2) (y '3))
(let ((xval x) (yval y))
(let ((getx-0 (lambda () xval))
(gety-0 (lambda () yval))
(display-0
(lambda () (begin (display "Coords: x=") (display xval) (display ",
y=") (display yval) (newline))))
(setx-1 (lambda (newx) (set! xval newx)))
(polar-0
(lambda ()
(let ((radius (sqrt (+ (* xval xval) (* yval yval))))
(angle (/ (truncate (* (atan (/ yval xval)) (/ 36000 (* 2
3.1415)))) 100)))
(list radius angle))))
(dist-1
(lambda (p2)
(let ((x2 (p2 'getx)) (y2 (p2 'gety))) (sqrt (+ (expt (- x2 xval)
2) (expt (- y2 yval) 2)))))))
(lambda msg (eval (cons (methods-append (car msg) (cdr msg) 0) (cdr
msg)))))))
Methods-append takes the method name and list of arguments, and appends the
number of arguments to the method name. Of course, the above returns a
function and if I were to bind that to point1 and call (point1 'getx), all I
get is an undefined identifier error for getx, since it is not defined
globally.
I've also constructed an alternative solution, which was much more convoluted,
which uses a long cond statement, with a check for each (appended) method
name. However, after remaking the new function, I haven't gotten it working
yet, but I realized I have no idea how to implement self-referential methods -
that is, methods in the object being able to call (*this* methodname args).
This would work naturally in my first solution, since the methods are defined,
but with the second, the cond statement, the "object" would have to call
itself. Normally, when I have a function call itself, it's because I'm
assigning it a label then referring to that label. Since I have to have "new"
only return a function, per the professor's specification, I can't know the
label.
If there's no solution to my eval problem, is there a way to have a function
call itself without any label?
Sorry for the long explanation for what boils down to a simple question, and
thanks in advance for any help.
- David Johnson