urmish
urmish

Reputation: 127

Requesting an explanation for unexpected behavior of set-car! and define in scheme

While learning scheme commands from the official documentation https://www.scheme.com/tspl4/start.html#./start:h9

What is going on here:

> (define ls1 '((ignored) ignored))
#<unspecified>
> ls1
((ignored) ignored)
> (set-car! (cdr ls1) 'a)
#<unspecified>
> ls1
((ignored) a)

This is expected. But when I use a define another list which 'looks' like ls1, in the following manner, why does set-car! replace both occurrences of ignored with a? I understand that the define end (cons 'ignored '()) is most likely the reason for the behavior but I can't formulate any explanation for this.

> (define end (cons 'ignored '()))
#<unspecified>
> end
(ignored)
> (define ls2 (cons end end))
#<unspecified>
> ((set-car! (cdr ls2) 'a)
#<unspecified>
> ls2
((a) a)
> 

Upvotes: 1

Views: 62

Answers (1)

Will Ness
Will Ness

Reputation: 71119

Let's say you had

(define end1 (cons 'ignored '()))
(define ls1 (cons end1 (cons 'ignored '())))
(define end2 (cons 'ignored '()))
(define ls2 (cons end2 end2))

As is readily seen, ls2 refers to the same entity named end2 with both it parts, the car and the cdr.

Quite differently, ls1's two parts are referring each to a different entity - the car refers to end1 and the cdr - to results of another invocation of the same cons form:

(eq? end2 end2) ;=> #t
(eq? (car ls2) (cdr ls2)) ;=> #t
(eq? (car ls1) (cdr ls1)) ;=> #f
(equal? (car ls1) (cdr ls1)) ;=> #t

The eq? returning #t means the two arguments are actually the same object in memory. So when you alter the contents of a memory-resident object, you alter the contents of a memory-resident object. With ls2, there's only one location involved, the one referred to by end2:

(eq? (cdr ls2) end2)       ;=> #t
(set-car! (cdr ls2) 'a)    ; changes `end2` to '(a)

Otherwise, each new invocation of cons is guaranteed to create and return a new location in memory, even if holding equal? contents. Hence altering one copy does not affect the other, situated at another place in memory:

(eq? (cdr ls1) end1)       ;=> #f
(set-car! (cdr ls1) 'a)    ; `end1` is unchanged

Upvotes: 2

Related Questions