开发者

How can I use external variables in eval in Scheme?

I am trying something out in Scheme for fun. I'm trying to make an Area function that gets the type of the thing it's operating on and then calls a different function depending on the type of object. Here's my code:

(define (area object)
  (if (not (null? (eval (word 'area- (get-type object)))))
      (eval (list (word 'area- (get-type object)) 'object))
      #f
  )
)

Scheme doesn't like this because it says object is an unbound variable. No, I can't take the quote away because then it's actually placing the value there and then Scheme complains that the list is malformed.

What can I do to use the value in object within eval?

Note: Scheme apparently grabs the global variable "object" just fine, so it's basically ignoring that it's inside a func开发者_高级运维tion.

Some information for a related language is here: http://docs.racket-lang.org/guide/eval.html, which seems to indicate that there isn't a solution in Scheme, but if you know of one I'd like to hear it.


There isn't one -- and that's a feature. eval is doing an evaluation of a form that was generated dynamically at runtime. So, if it needs to know about local bindings, then you'd need to compile (lamba (x) x) and (lambda (y) y) differently -- because the name matter. But this is just the tip, there's a whole bunch of issues around implementing this kind of feature.

As for your problem -- even if it was possible to do what you want to, it's a fragile solution that depends on name. Remember that in Scheme you can use functions like any other value -- so instead of calling get-type and combining it with some symbol to get a name, make your objects contain the function that is needed (which at that point would be better called "method").

Something like:

(define (area object)
  ((get-area-method object) object))

Obviously doing this means that there's little point in not going the whole way with:

(define (area object)
  (get-area object))

which is just

(define area get-area)

But the first might be more typical of a general OO-like system, with a way to get methods, so it might be useful for you. That direction could take you to:

(define (area object)
  ((get-method object 'get-area) object))


Racket has classes and methods, and you should use it!

(define circle%
  (class object%
    (init radius)
    (define r radius)
    (super-new)
    (define/public (area)
      (* pi r r))))

(define rectangle%
  (class object%
    (init width height)
    (define w width)
    (define h height)
    (super-new)
    (define/public (area)
      (* w h))))

(define unit-circle (new circle% [radius 1]))
(define unit-square (new rectangle% [width 1] [height 1]))

(send unit-circle area)   ; => 3.141592653589793
(send unit-square area)   ; => 1

Much less hacky than name-based dispatch.


Apparently there IS a way to do what I wanted to do in Scheme. Here is the code:

(define (area object)
  ((eval (list 'identity (word 'area- (get-type object)))) object)
)

Basically the trick is this: Because eval only knows about global variables, I can still use the identity function within eval to return the value of a global variable. The global variable I'm interested in in this case is a function, which is just like any other variable. I can then return this value and use it as the procedure to call on my original argument. This lets me construct the name of the variable I want to get from global scope and get and use the procedure contained in that variable, accomplishing the result I was looking for.

Here's a cleaner version:

(define (area object)
  ((get-function (word 'area- (get-type object))) object)
)

(define (get-function function)
  (eval (list 'identity function)))

Apparently the is-not-null part will not work, however, because trying to get the identity of a function that doesn't exist causes an unbound variable error. So, still need to be careful to call the operator on a type that supports it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜