1

Consider the following code snippet:

(define (make)
  (define b '(bottom))
  b)

(eqv? (make) (make)) ; => #t

Why is it so? I was expecting to get a different copy of the same object with each invocation of "make" (i.e. placed at different memory addresses), yet it seems that all of them are identical (i.e. stored at the same address).

To make things even more confusing, consider the following only slightly different code:

(define (make)
  (define b '(bottom))
  (cons b b))

(eqv? (make) (make)) ; => #f

Why are the two copies different now (i.e. at different addresses)?

Alex M.
  • 483
  • 11
  • 23
  • Have a look at [Unexpected persistence of data](http://stackoverflow.com/q/18790192/1281433). People often encounter this problem when they *modify* the value and see the changes in unexpected places. You've been proactive, and are detecting the equality even without a modification. – Joshua Taylor Jan 02 '15 at 13:32

2 Answers2

3

quote defines a literal expression. Think of it as a compile-time constant: '(bottom) builds the one-element list at the time the program is compiled. The same one-element list is used throughout the execution of the program. Each time (define b '(bottom)) is executed, it sets b to the same value, in the sense that it's a pointer to the same compile-time constant (which may be stored in a read-only memory area). Thus (eqv? (make) (make)) compares two pointers to the same constant, and they are equal.

In the second example, the result of (make) is the result of calling cons. Each call to cons returns a fresh cell, so (eqv? (make) (make)) is false.

R6RS references: 11.4.1 quote defines a constant; 5.10 constants are immutable and have a single memory location; 11.5 eqv returns #t when its two arguments have the same location; 11.9 cons returns a newly allocated object.

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
1

'(bottom), because it is quoted, is considered a constant in Scheme. Everything that is quoted may share structure as well. eg.:

(define one '(bottom))
(define two '(bottom))

(eq? one two) ; ==> undefined

With undefined you can expect one scheme implementation to result in #f and another to result in #t. Even the same implementation might change depending on compiler settings.

To get your desired behavior you need to cons in your procedure. Below is a version that uses list to create unique lists for each call:

(define (make)
  (list 'bottom))

;; test
(eq? (make) (make))    ; ==> #f
(eqv? (make) (make))   ; ==> #f
(equal? (make) (make)) ; ==> #t

If you want to use constants as a template you can use list-copy:

(define (make)
  (list-copy '(bottom)))

Also note that constants are immutable. Strings in double quotes are also like quotes lists. In many of the Scheme reports violating the report may not signal an error, just start to behave strange. eg.

(define constant "horse")
(string-set! constant 0 #\H)

According to R5RS the above two lines are not Scheme since string-set! is mutating a constant. In R6RS it should cause an exception with condition type &assertion to be raised. However, very few of the implementations do. You get the following effects:

(define (get-constant)
   "horse")
(define constant (get-constant))
(string-set! (get-constant) 0 #\H)
constant       ; ==> "Horse"
(get-constant) ; ==> "Horse"

EDIT

To comment on your addition:

(define (make)
  (define b '(bottom))
  (cons b b))

Here b is still constant but the pair that holds the two are new at every execution just like in my version that used list ((list 'bottom) is the same as (cons 'bottom '())):

(eq? (make) (make))             ; ==> #f
(eq? (car (make)) (car (make))) ; ==> #t
Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • How then to explain that (eqv? '(bottom) '(bottom)) returns #f? If '(bottom) were an immutable constant, shouldn't it return #t? And one more question: isn't '(a b c) the same as (list 'a 'b 'c)? At least, this is how [DrRacket treats things](http://www.ccs.neu.edu/home/matthias/HtDP2e/i2-3.html). – Alex M. Jan 01 '15 at 16:53
  • @AlexM. Like I said "may share". `(eqv? '(bottom) '(bottom))` is undefined so the implementation decides if it wants to make both `'(bottom)` point to the same constant or that it makes two immutable constants which different objects that are `equal?`. Remember that Racket only follows Scheme standard if you tell it to (`#!r6rs` or `#!r5rs`). The default language (`#!racket`) isn't following any Scheme report and therefor **isn't Scheme**. – Sylwester Jan 01 '15 at 17:51
  • @AlexM. `'(a b c)` and `(list 'a 'b 'c)` both evaluate to objects that look the same and are `equal?`, but they are never the same object, even in Racket. The reason is that `(list 'a 'b 'c)` is guaranteed to be unique, thus `(eqv? (list 'a 'b 'c) (list 'a 'b 'c)) ; ==> #f` since they are made at two different places in the code. – Sylwester Jan 01 '15 at 18:05
  • I see. One more thing: if I invoke (list 'a) several times, are the results *guaranteed* to reside at different places in memory? The thing is that I need to create different lists with a single element (not necessarily the same element), and I was trying to find lighter alternatives to using (list (gensym)) for fear of remaining short of unique symbols in case of *really many* invocations. – Alex M. Jan 01 '15 at 18:24
  • One last thing: how does one add code formatting in comments? I'm new around and I only manage to do it in questions and answers, but not in comments. – Alex M. Jan 01 '15 at 18:25
  • Yes `(list 'a)` is guaranteed to be new each time (but not the `car` , `a`). `gensym` is to make a unique symbol since `a` is not unique. if you do `(eq? (car (list 'a)) (car (list 'a))) ; ==> #t` since even though the lists are unique their contents aren't. You get code formatting by using backticks `` around the code. – Sylwester Jan 01 '15 at 20:34