3

I am trying to implement the Metacircular Evaluator in Scheme according to the well-known book "Structure and Interpretation of Computer Programs" by Harold Abelson and Gerald Jay Sussman.

http://mitpress.mit.edu/sicp/full-text/sicp/book/node79.html, http://mitpress.mit.edu/sicp/full-text/sicp/book/node80.html

Authors suggest to setup environment in this way:

(define (define-variable! var val env)
  (let ((frame (first-frame env)))
    (define (scan vars vals)
      (cond ((null? vars)
             (add-binding-to-frame! var val frame))
            ((eq? var (car vars))
             (set-car! vals val))
            (else (scan (cdr vars) (cdr vals)))))
    (scan (frame-variables frame)
          (frame-values frame))))

(define (setup-environment)
  (let ((initial-env
         (extend-environment (primitive-procedure-names)
                             (primitive-procedure-objects)
                             the-empty-environment)))
    (define-variable! 'true true initial-env)
    (define-variable! 'false false initial-env)
    initial-env))

However, I can’t understand why

(define myenv (setup-environment))

should work as we expect in Scheme, because, as I know, Scheme by default passing variables to function by value, therefore after two times applying “define-variable!” to initial-env, initial-env won't be changed each time, and the setup-environment function will return the value as the extend-environment has returned it.

Where is my mistake in understanding, could you advise, please?

Thank you in advance!

Yura Perov
  • 105
  • 1
  • 1
  • 7

2 Answers2

5

Your question could be a teensy bit more specific, but I believe I understand it.

Specifically, your question appears to be this:

"I'm surprised by the behavior of

(define myenv (setup-environment))
(define-variable! 'a 13 myenv)
(lookup myenv 'a)

Specifically, I would expect it to fail, because Scheme is call-by-value." Is this your question?

If so, then I think I can answer it. Call-by-value doesn't mean that values can't change. It just means that function calls involve passing values from caller to callee. In fact, nearly all languages are call-by-value; this term is widely misunderstood. Java, for instance, is also a call-by-value language.

There's nothing about Scheme, then, that prevents you from changing, or "mutating", a value. In this example, the set-car! call mutates the list that it's referring to. This change is then visible to any piece of code that can "see" this value.

I think your fundamental question really has to do with what "call-by-value" means, and I hope I've shed some light on it.

John Clements
  • 16,895
  • 3
  • 37
  • 52
  • Yeah, a lot of people think languages are not "pass-by-value", when really they are, they're just "pass-reference-by-value". – Seth Carnegie Mar 16 '12 at 22:36
  • Thank you very much! You are right, I was not very accurate in my question, excuse me, please, for it. Will try to be clearer next time. – Yura Perov Mar 17 '12 at 11:07
2

To understand how this works, first of all you have to understand that the variable initial-env will point to the first frame of the environment, and this reference is never modified. The environment itself is a list of frames, and each frame is a pair of lists, the car of the first frame is the head of the list of variables, and the cdr of the first frame is the head of the list of values.

Once that is clear, you need to be aware of how the procedures scan and add-binding-to-frame! work. Scan will look inside the current frame, in the list of variables, for a variable with the same name as var; if found it will replace the corresponding value in the list of values. If the variable wasn't found, add-binding-to-frame! will add a new variable and a new value at the head of the corresponding lists, updating the frame to point to this new heads. Notice that initial-environment is still pointing to the first frame, and the first frame is still pointing to the heads of its lists of variables and values (but with a new binding).

So you see now, even though initial-env was never changed, the lists it contains were modified in place, therefore adding new variables with their respective values. I believe the best way to understand the whole process is to grab a pen and a paper and to draw, step-by-step, the result of modifying the cons cells involved.

Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • @Alfucio if this answer was helpful in any way for you, an upvote would be nice :) – Óscar López Mar 17 '12 at 13:04
  • Yes, I'm very sorry for two things: firstly, that I am not able to mark your answer as an accepted too (tttt, it is very strange for me that I'm not able to it here, because I believe there're often many accepted answers, e.g., as for my current question), although I consider your answer as well helpful as first one (and I decided to mark first one only because it was first :) ); secondly, that I am not able to upvote your answer now, because I still have only 13 reputation points. I will surely upvote your post as soon I will receive 15 p. Again, thank you very much, it was really helpful! – Yura Perov Mar 17 '12 at 16:17