Reputation: 23
I have a problem understanding destructive operations within scheme that don't seem consistent. Namely why bar doesn't change in the following example
(define foo '(a b))
(define bar foo)
(set! foo '(c d))
foo
>(c d)
bar
>(a b)
However this makes the bar change by changing foo
(define foo '(a b))
(define bar foo)
(set-car! foo 'c)
foo
>(c b)
bar
>(c b)
If I understand correctly, bar is pointing at foo in its' car in both examples, but somehow the old foo does not change while using set! in the first one. I can't find an answer anywhere about how and why this happens.
Upvotes: 2
Views: 311
Reputation: 136
Aliasing doesn't mean that bar
points to foo
, but rather that bar
points to the same thing that foo
points to at the moment. If pictures help, here are box and arrow diagrams for what's going on. After your setup code, you have this:
(define foo (list 'a 'b))
(define bar foo)
Now if you run your first try, you create a new list and set foo
to point to it without changing anything about bar
or what it references.
(set! foo (list 'c 'd))
But if you run this code instead, you modify the (shared) list that both foo
and bar
happen to point to, at the moment.
(set-car! foo 'c)
Upvotes: 5
Reputation: 48745
define
makes a variable. A name that evaluates to a location of an object.
(define foo (list 'a 'b))
Here foo
might point to address 240
. When evaluateing foo
you get 240
back so (define bar foo)
will make a second variable that point to the same object in memory. Addresses are not shown in the REPL so you'll se (a b)
since the REPL prints whats in address 240
.
This object in memory is a cons
so each of those are just two pointer to other values in memory. Thus when you do:
(set-car foo 'c)
you are changing the cons located at address 240 by changing the car
pointer from the address representation of a
to the address representation of c
. Since both foo
and bar
points to the same cons cell at address 240
they both will get the change since they point to the same data.
If you use set!
you only change what value a name is pointing to:
(set! foo (cons 'd (cdr foo)))
Now this will update the variables pointer from pointing to 240
to some other address. Thus you can evaluate foo
and bar
and see they are different values:
foo ; ==> (d b)
bar ; ==> (c b)
Here foo
might be located in address 256
. Since we used the old values cdr
in foo
we know that the tail of those are the same:
(cdr foo) ; ==> (b)
(eq? (cdr foo) (cdr bar)) ; ==> #t
If you set!
bar
to a different value:
(set! bar foo)
Now both foo
and bar
point to 256
and if there are no longer uses of 240
the pair at that location might be garbage collected but since cdr
of the original foo
is the cdr
, lets say address 248
, that address will not be garbage collected since it is pointed to by the cons
in address 256
.
NB: I've changed the definition of foo
since you are not allowed to set-car
literals like '(a b)
since they are immutable. (list 'a 'b)
creates a fresh list every time that line of code is executed and you can be assured the address is not being reused in eg. '(d a b)
.
NB2: The Scheme report tries its best to keep away from dictating how to store values. Thus they don't use the concept address in the report. Most implementations of both Scheme and CL models their data quite similar.
Upvotes: 1