Search code examples
lispschemeracketmit-scheme

set-car! and let in scheme language


I am little bit confused by the result of this example:

(define mk-q
  (lambda ()
    (let ([l '(x)])
      (cons l l))))


(define q (mk-q))

q
=> ((x) x)

(set-car! (cdr q) 'y)
=> ((y) y)

I am wondering why both x atoms have been replaced by set-car! procedure (my first guess for what the result would be was ((x) y))?

For example:

(define mk-q2
  (lambda ()
    (let ([l '(x)])
      (cons l (cons l l)))))

(define q2 (mk-q2))
(set-car! (cdr q2) 'y)
=> ((x) y x) which fits my understanding of set-car!

Why are both xs in the first example replaced?


Solution

  • In the first example, you have something equivalent to this:

    (define cell (cons 'x null))
    (define q (cons cell cell))
    

    As you can see, there's only one cons cell with x at the car position, that is being shared in two different parts of the resulting list structure. When you execute (set-car! (cdr q) 'y) the x in the single cell gets replaced by y in all the parts where it's being shared. Remembering that both (cons 'x null) cells are really the same, we're going from this:

    (cons (cons 'x null) (cons 'x null))
    ; '((x) x)
    

    to this:

    (cons (cons 'y null) (cons 'y null))
    ; '((y) y)
    

    For the second example the same considerations apply (all three (cons 'x null) cells are actually the same one being shared), but you're replacing a whole cons cell, so basically we're going from this:

    (cons (cons 'x null) (cons (cons 'x null) (cons 'x null)))
    ; '((x) (x) x)
    

    to this:

    (cons (cons 'x null) (cons 'y (cons 'x null)))
    ; '((x) y x)
    

    To prove my point that both of the examples in the question demonstrate the same situation, execute this expressions:

    (define q2 (mk-q2))
    (set-car! (cadr q2) 'y) ; notice the extra `a`
    q2
    => '((y) (y) y)