Using let variables in a lambda in Scheme

2019-07-20 14:22发布

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)))))

标签: scope scheme
2条回答
唯我独甜
2楼-- · 2019-07-20 14:36

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:

(define (hello m)
  (let ((msg m))
    (eval '(print msg))))

Making let part of eval make it works:

(define (hello m)
  (eval `(let ((msg ,m))
           (print msg))))
查看更多
Luminary・发光体
3楼-- · 2019-07-20 14:44

This doesn't implement all the syntax you have in mind, but it might illustrate the techniques needed to implement it cleanly.

(define-syntax make-object
  (syntax-rules ()
    [(__ ([ivar ival] ...) ([method-name args body ...] ...))
      (let ([ivar ival] ...)
        (λ (msg . oargs)
          (cond
            [(eq? 'method-name msg)
              (apply (λ args body ...) oargs)] ...
            [else
              (error 'object-system "unknown message" msg)])))]))

(define o (make-object ([xvalue 'x])
                       ([getx () xvalue]
                        [setx (x) (set! xvalue x)])))

(o 'getx) => x
(o 'setx 'blah)
(o 'getx) => blah

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.

查看更多
登录 后发表回答