unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* Surprising behavior of eq?
@ 2020-09-20 12:16 Zelphir Kaltstahl
  2020-09-20 12:19 ` Zelphir Kaltstahl
  2020-09-20 17:26 ` Taylan Kammer
  0 siblings, 2 replies; 13+ messages in thread
From: Zelphir Kaltstahl @ 2020-09-20 12:16 UTC (permalink / raw)
  To: Guile User

Hello Guile users,

I just noticed something weird about eq?.

My Guile version is:


I get the different results, depending on whether I define some bindings
in a let or using define:

(In Emacs Geiser:)

~~~~
(define x '(10 9))
(define y '(10 9))
(eq? x y)
$2 = #f

(let ([x '(10 9)]
      [y '(10 9)])
     (eq? x y))
$3 = #t
~~~~

Is this intentional or a bug?

I first noticed something strange when writing the following code:

~~~~DEFINITION~~~~
(define make-multiple-list-remover
  (λ (equal-proc)
    (λ (lst unwanted)
      (let loop ([remaining-list lst])
        (cond
         [(null? remaining-list)
          '()]
         [(equal-proc (car remaining-list) unwanted)
          (loop (cdr remaining-list))]
         [else
          (cons (car remaining-list)
                (loop (cdr remaining-list)))])))))
~~~~

~~~~TEST~~~~
(let ([a '(9 10)]
      [b '(9 10)])
  (test-equal "make-multiple-list-remover-03"
    `(1 2 (3) (4) ,a)
    ((make-multiple-list-remover eq?)
     `(a b (c) (d) ,a) b)))
~~~~

I was wondering, why the test fails. I think (eq? ...) should not be
able to see the equivalence of both lists a and b, just like when
defined using (define ...).

I can also run it in the REPL and see the difference:

~~~~
(define a '(9 10))
(define b '(9 10))
((make-multiple-list-remover eq?)
 `(a b (c) (d) ,a) b)
$4 = (a b (c) (d) (9 10))

(let ([a '(9 10)]
      [b '(9 10)])
  ((make-multiple-list-remover eq?)
   `(a b (c) (d) ,a) b))
$5 = (a b (c) (d))
~~~~

Somehow the bindings of let seem to be different from the bindings
created using define. What about using define inside let?

~~~~

~~~~

-- 
repositories: https://notabug.org/ZelphirKaltstahl



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Surprising behavior of eq?
  2020-09-20 12:16 Surprising behavior of eq? Zelphir Kaltstahl
@ 2020-09-20 12:19 ` Zelphir Kaltstahl
  2020-09-20 12:58   ` Christopher Lemmer Webber
  2020-09-20 13:09   ` Zelphir Kaltstahl
  2020-09-20 17:26 ` Taylan Kammer
  1 sibling, 2 replies; 13+ messages in thread
From: Zelphir Kaltstahl @ 2020-09-20 12:19 UTC (permalink / raw)
  To: Guile User

Sorry, I misclicked "send" when I wanted to further edit my e-mail ...

My Guile version is:

~~~~
(version)
$6 = "3.0.4"
~~~~

On 20.09.20 14:16, Zelphir Kaltstahl wrote:
>
> Hello Guile users,
>
> I just noticed something weird about eq?.
>
> My Guile version is:
>
>
> I get the different results, depending on whether I define some
> bindings in a let or using define:
>
> (In Emacs Geiser:)
>
> ~~~~
> (define x '(10 9))
> (define y '(10 9))
> (eq? x y)
> $2 = #f
>
> (let ([x '(10 9)]
>       [y '(10 9)])
>      (eq? x y))
> $3 = #t
> ~~~~
>
> Is this intentional or a bug?
>
> I first noticed something strange when writing the following code:
>
> ~~~~DEFINITION~~~~
> (define make-multiple-list-remover
>   (λ (equal-proc)
>     (λ (lst unwanted)
>       (let loop ([remaining-list lst])
>         (cond
>          [(null? remaining-list)
>           '()]
>          [(equal-proc (car remaining-list) unwanted)
>           (loop (cdr remaining-list))]
>          [else
>           (cons (car remaining-list)
>                 (loop (cdr remaining-list)))])))))
> ~~~~
>
> ~~~~TEST~~~~
> (let ([a '(9 10)]
>       [b '(9 10)])
>   (test-equal "make-multiple-list-remover-03"
>     `(1 2 (3) (4) ,a)
>     ((make-multiple-list-remover eq?)
>      `(a b (c) (d) ,a) b)))
> ~~~~
>
> I was wondering, why the test fails. I think (eq? ...) should not be
> able to see the equivalence of both lists a and b, just like when
> defined using (define ...).
>
> I can also run it in the REPL and see the difference:
>
> ~~~~
> (define a '(9 10))
> (define b '(9 10))
> ((make-multiple-list-remover eq?)
>  `(a b (c) (d) ,a) b)
> $4 = (a b (c) (d) (9 10))
>
> (let ([a '(9 10)]
>       [b '(9 10)])
>   ((make-multiple-list-remover eq?)
>    `(a b (c) (d) ,a) b))
> $5 = (a b (c) (d))
> ~~~~
>
> Somehow the bindings of let seem to be different from the bindings
> created using define. What about using define inside let?
>
> ~~~~
>
> ~~~~
> -- 
> repositories: https://notabug.org/ZelphirKaltstahl

Somehow the bindings of let seem to be different from the bindings
created using define. What about using define inside let?

~~~~
(let ([unrelated 'bla])
  (define a '(9 10))
  (define b '(9 10))
  ((make-multiple-list-remover eq?)
   `(a b (c) (d) ,a) b))
$7 = (a b (c) (d))
~~~~

So there the define usage also differs from when I use define on the top
level. Perhaps that is the difference? On which level the bindings are
defined?

Regards,
Zelphir

-- 
repositories: https://notabug.org/ZelphirKaltstahl



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Surprising behavior of eq?
  2020-09-20 12:19 ` Zelphir Kaltstahl
@ 2020-09-20 12:58   ` Christopher Lemmer Webber
  2020-09-20 13:09   ` Zelphir Kaltstahl
  1 sibling, 0 replies; 13+ messages in thread
From: Christopher Lemmer Webber @ 2020-09-20 12:58 UTC (permalink / raw)
  To: Zelphir Kaltstahl; +Cc: guile-user

My guess, without really knowing, is this is some sort of optimization.
But it could affect things I suppose if someone is intending to
construct lists for the main purpose of creating different
eq-differentiated identifiers (eg as keys for a hasheq)...

Zelphir Kaltstahl writes:

> Sorry, I misclicked "send" when I wanted to further edit my e-mail ...
>
> My Guile version is:
>
> ~~~~
> (version)
> $6 = "3.0.4"
> ~~~~
>
> On 20.09.20 14:16, Zelphir Kaltstahl wrote:
>>
>> Hello Guile users,
>>
>> I just noticed something weird about eq?.
>>
>> My Guile version is:
>>
>>
>> I get the different results, depending on whether I define some
>> bindings in a let or using define:
>>
>> (In Emacs Geiser:)
>>
>> ~~~~
>> (define x '(10 9))
>> (define y '(10 9))
>> (eq? x y)
>> $2 = #f
>>
>> (let ([x '(10 9)]
>>       [y '(10 9)])
>>      (eq? x y))
>> $3 = #t
>> ~~~~
>>
>> Is this intentional or a bug?
>>
>> I first noticed something strange when writing the following code:
>>
>> ~~~~DEFINITION~~~~
>> (define make-multiple-list-remover
>>   (λ (equal-proc)
>>     (λ (lst unwanted)
>>       (let loop ([remaining-list lst])
>>         (cond
>>          [(null? remaining-list)
>>           '()]
>>          [(equal-proc (car remaining-list) unwanted)
>>           (loop (cdr remaining-list))]
>>          [else
>>           (cons (car remaining-list)
>>                 (loop (cdr remaining-list)))])))))
>> ~~~~
>>
>> ~~~~TEST~~~~
>> (let ([a '(9 10)]
>>       [b '(9 10)])
>>   (test-equal "make-multiple-list-remover-03"
>>     `(1 2 (3) (4) ,a)
>>     ((make-multiple-list-remover eq?)
>>      `(a b (c) (d) ,a) b)))
>> ~~~~
>>
>> I was wondering, why the test fails. I think (eq? ...) should not be
>> able to see the equivalence of both lists a and b, just like when
>> defined using (define ...).
>>
>> I can also run it in the REPL and see the difference:
>>
>> ~~~~
>> (define a '(9 10))
>> (define b '(9 10))
>> ((make-multiple-list-remover eq?)
>>  `(a b (c) (d) ,a) b)
>> $4 = (a b (c) (d) (9 10))
>>
>> (let ([a '(9 10)]
>>       [b '(9 10)])
>>   ((make-multiple-list-remover eq?)
>>    `(a b (c) (d) ,a) b))
>> $5 = (a b (c) (d))
>> ~~~~
>>
>> Somehow the bindings of let seem to be different from the bindings
>> created using define. What about using define inside let?
>>
>> ~~~~
>>
>> ~~~~
>> -- 
>> repositories: https://notabug.org/ZelphirKaltstahl
>
> Somehow the bindings of let seem to be different from the bindings
> created using define. What about using define inside let?
>
> ~~~~
> (let ([unrelated 'bla])
>   (define a '(9 10))
>   (define b '(9 10))
>   ((make-multiple-list-remover eq?)
>    `(a b (c) (d) ,a) b))
> $7 = (a b (c) (d))
> ~~~~
>
> So there the define usage also differs from when I use define on the top
> level. Perhaps that is the difference? On which level the bindings are
> defined?
>
> Regards,
> Zelphir




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Surprising behavior of eq?
  2020-09-20 12:19 ` Zelphir Kaltstahl
  2020-09-20 12:58   ` Christopher Lemmer Webber
@ 2020-09-20 13:09   ` Zelphir Kaltstahl
  2020-09-20 13:52     ` John Cowan
  2020-09-20 13:57     ` Stefan Schmiedl
  1 sibling, 2 replies; 13+ messages in thread
From: Zelphir Kaltstahl @ 2020-09-20 13:09 UTC (permalink / raw)
  To: guile-user

And I've noticed something more about equality stuff in the context of
tests:

~~~~
(eqv? "a" "a")
$3 = #t

;; but

(define char->string
  (λ (c)
    (list->string
     (list c))))

(import
  ;; unit tests
  (srfi srfi-64))

(test-begin "string-utils-test")

(test-group
 "char-to-string-test"

 (test-eqv "char->string converts a character to a string"
   "a"
   (char->string #\a)))

(test-end "string-utils-test")

%%%% Starting test string-utils-test  (Writing full log to "string-utils-test.log")
$2 = ("string-utils-test")
:19: FAIL char->string converts a character to a string
# of unexpected failures  1
~~~~

So while (eqv? ...) gives the correct (?) result, the test procedure
(test-eqv ...) which seems to indicate using (eqv? ...) via its name
does not think of the two strings as equivalent.


On 20.09.20 14:19, Zelphir Kaltstahl wrote:
> Sorry, I misclicked "send" when I wanted to further edit my e-mail ...
>
> My Guile version is:
>
> ~~~~
> (version)
> $6 = "3.0.4"
> ~~~~
>
> On 20.09.20 14:16, Zelphir Kaltstahl wrote:
>> Hello Guile users,
>>
>> I just noticed something weird about eq?.
>>
>> My Guile version is:
>>
>>
>> I get the different results, depending on whether I define some
>> bindings in a let or using define:
>>
>> (In Emacs Geiser:)
>>
>> ~~~~
>> (define x '(10 9))
>> (define y '(10 9))
>> (eq? x y)
>> $2 = #f
>>
>> (let ([x '(10 9)]
>>       [y '(10 9)])
>>      (eq? x y))
>> $3 = #t
>> ~~~~
>>
>> Is this intentional or a bug?
>>
>> I first noticed something strange when writing the following code:
>>
>> ~~~~DEFINITION~~~~
>> (define make-multiple-list-remover
>>   (λ (equal-proc)
>>     (λ (lst unwanted)
>>       (let loop ([remaining-list lst])
>>         (cond
>>          [(null? remaining-list)
>>           '()]
>>          [(equal-proc (car remaining-list) unwanted)
>>           (loop (cdr remaining-list))]
>>          [else
>>           (cons (car remaining-list)
>>                 (loop (cdr remaining-list)))])))))
>> ~~~~
>>
>> ~~~~TEST~~~~
>> (let ([a '(9 10)]
>>       [b '(9 10)])
>>   (test-equal "make-multiple-list-remover-03"
>>     `(1 2 (3) (4) ,a)
>>     ((make-multiple-list-remover eq?)
>>      `(a b (c) (d) ,a) b)))
>> ~~~~
>>
>> I was wondering, why the test fails. I think (eq? ...) should not be
>> able to see the equivalence of both lists a and b, just like when
>> defined using (define ...).
>>
>> I can also run it in the REPL and see the difference:
>>
>> ~~~~
>> (define a '(9 10))
>> (define b '(9 10))
>> ((make-multiple-list-remover eq?)
>>  `(a b (c) (d) ,a) b)
>> $4 = (a b (c) (d) (9 10))
>>
>> (let ([a '(9 10)]
>>       [b '(9 10)])
>>   ((make-multiple-list-remover eq?)
>>    `(a b (c) (d) ,a) b))
>> $5 = (a b (c) (d))
>> ~~~~
>>
>> Somehow the bindings of let seem to be different from the bindings
>> created using define. What about using define inside let?
>>
>> ~~~~
>>
>> ~~~~
>> -- 
>> repositories: https://notabug.org/ZelphirKaltstahl
> Somehow the bindings of let seem to be different from the bindings
> created using define. What about using define inside let?
>
> ~~~~
> (let ([unrelated 'bla])
>   (define a '(9 10))
>   (define b '(9 10))
>   ((make-multiple-list-remover eq?)
>    `(a b (c) (d) ,a) b))
> $7 = (a b (c) (d))
> ~~~~
>
> So there the define usage also differs from when I use define on the top
> level. Perhaps that is the difference? On which level the bindings are
> defined?
>
> Regards,
> Zelphir
>
-- 
repositories: https://notabug.org/ZelphirKaltstahl



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Surprising behavior of eq?
  2020-09-20 13:09   ` Zelphir Kaltstahl
@ 2020-09-20 13:52     ` John Cowan
  2020-09-20 15:37       ` Zelphir Kaltstahl
  2020-09-20 13:57     ` Stefan Schmiedl
  1 sibling, 1 reply; 13+ messages in thread
From: John Cowan @ 2020-09-20 13:52 UTC (permalink / raw)
  To: Zelphir Kaltstahl; +Cc: guile-user

What's happening is that you are trying to compare strings with an
inappropriate predicate.  Eqv? tests for *identity* (being the same
object), whereas equal? tests for *equality* (havng the same structure).
Therefore, (equal? "foo" "foo") will always return #t, and almost all the
time that's the predicate you should use.

The question then is: when are two strings identical and not merely equal?
In the general case, they are identical if they are the result of a single
constructor.  Thus given:

(define a (string #\a #\b #\c))
(define b a)

then (eqv? a b) => #t

But given

(define c (string #\a #\b #c))
(define d (string #\a #\b #c))

then (eqv? c  d) => #f

Finally, we have to deal with string literals.  If there is more than one
appearance of "xyz" in code, they are all equal, but are they identical?
The answer is that it is implementation-dependent.  A Scheme is free to
consolidate all equal literals into the same object.  It looks like Guile
does so when they both appear in the same top-level expression, but *not*
in different top-level expressions.  So given

(define e "xyz")
(define f "xyz")

then (eqv? e f) => #f

But (let ((g "xyz") (h "xyz"))  (eqv? g h)) => #t, because Guile
consolidates the two equal literals into a single object.  If you try this
on Chibi Scheme, you'll get #f, because Chibi does *not* consolidate string
literals.

I hope that is helpful.




On Sun, Sep 20, 2020 at 9:11 AM Zelphir Kaltstahl <
zelphirkaltstahl@posteo.de> wrote:

> And I've noticed something more about equality stuff in the context of
> tests:
>
> ~~~~
> (eqv? "a" "a")
> $3 = #t
>
> ;; but
>
> (define char->string
>   (λ (c)
>     (list->string
>      (list c))))
>
> (import
>   ;; unit tests
>   (srfi srfi-64))
>
> (test-begin "string-utils-test")
>
> (test-group
>  "char-to-string-test"
>
>  (test-eqv "char->string converts a character to a string"
>    "a"
>    (char->string #\a)))
>
> (test-end "string-utils-test")
>
> %%%% Starting test string-utils-test  (Writing full log to
> "string-utils-test.log")
> $2 = ("string-utils-test")
> :19: FAIL char->string converts a character to a string
> # of unexpected failures  1
> ~~~~
>
> So while (eqv? ...) gives the correct (?) result, the test procedure
> (test-eqv ...) which seems to indicate using (eqv? ...) via its name
> does not think of the two strings as equivalent.
>
>
> On 20.09.20 14:19, Zelphir Kaltstahl wrote:
> > Sorry, I misclicked "send" when I wanted to further edit my e-mail ...
> >
> > My Guile version is:
> >
> > ~~~~
> > (version)
> > $6 = "3.0.4"
> > ~~~~
> >
> > On 20.09.20 14:16, Zelphir Kaltstahl wrote:
> >> Hello Guile users,
> >>
> >> I just noticed something weird about eq?.
> >>
> >> My Guile version is:
> >>
> >>
> >> I get the different results, depending on whether I define some
> >> bindings in a let or using define:
> >>
> >> (In Emacs Geiser:)
> >>
> >> ~~~~
> >> (define x '(10 9))
> >> (define y '(10 9))
> >> (eq? x y)
> >> $2 = #f
> >>
> >> (let ([x '(10 9)]
> >>       [y '(10 9)])
> >>      (eq? x y))
> >> $3 = #t
> >> ~~~~
> >>
> >> Is this intentional or a bug?
> >>
> >> I first noticed something strange when writing the following code:
> >>
> >> ~~~~DEFINITION~~~~
> >> (define make-multiple-list-remover
> >>   (λ (equal-proc)
> >>     (λ (lst unwanted)
> >>       (let loop ([remaining-list lst])
> >>         (cond
> >>          [(null? remaining-list)
> >>           '()]
> >>          [(equal-proc (car remaining-list) unwanted)
> >>           (loop (cdr remaining-list))]
> >>          [else
> >>           (cons (car remaining-list)
> >>                 (loop (cdr remaining-list)))])))))
> >> ~~~~
> >>
> >> ~~~~TEST~~~~
> >> (let ([a '(9 10)]
> >>       [b '(9 10)])
> >>   (test-equal "make-multiple-list-remover-03"
> >>     `(1 2 (3) (4) ,a)
> >>     ((make-multiple-list-remover eq?)
> >>      `(a b (c) (d) ,a) b)))
> >> ~~~~
> >>
> >> I was wondering, why the test fails. I think (eq? ...) should not be
> >> able to see the equivalence of both lists a and b, just like when
> >> defined using (define ...).
> >>
> >> I can also run it in the REPL and see the difference:
> >>
> >> ~~~~
> >> (define a '(9 10))
> >> (define b '(9 10))
> >> ((make-multiple-list-remover eq?)
> >>  `(a b (c) (d) ,a) b)
> >> $4 = (a b (c) (d) (9 10))
> >>
> >> (let ([a '(9 10)]
> >>       [b '(9 10)])
> >>   ((make-multiple-list-remover eq?)
> >>    `(a b (c) (d) ,a) b))
> >> $5 = (a b (c) (d))
> >> ~~~~
> >>
> >> Somehow the bindings of let seem to be different from the bindings
> >> created using define. What about using define inside let?
> >>
> >> ~~~~
> >>
> >> ~~~~
> >> --
> >> repositories: https://notabug.org/ZelphirKaltstahl
> > Somehow the bindings of let seem to be different from the bindings
> > created using define. What about using define inside let?
> >
> > ~~~~
> > (let ([unrelated 'bla])
> >   (define a '(9 10))
> >   (define b '(9 10))
> >   ((make-multiple-list-remover eq?)
> >    `(a b (c) (d) ,a) b))
> > $7 = (a b (c) (d))
> > ~~~~
> >
> > So there the define usage also differs from when I use define on the top
> > level. Perhaps that is the difference? On which level the bindings are
> > defined?
> >
> > Regards,
> > Zelphir
> >
> --
> repositories: https://notabug.org/ZelphirKaltstahl
>
>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Surprising behavior of eq?
  2020-09-20 13:09   ` Zelphir Kaltstahl
  2020-09-20 13:52     ` John Cowan
@ 2020-09-20 13:57     ` Stefan Schmiedl
  2020-09-20 15:42       ` Zelphir Kaltstahl
  1 sibling, 1 reply; 13+ messages in thread
From: Stefan Schmiedl @ 2020-09-20 13:57 UTC (permalink / raw)
  To: Zelphir Kaltstahl, guile-user

Let's have a little shop talk with guile:

$ guile-3.0
GNU Guile 3.0.4-dirty
scheme@(guile-user)> (eqv? "a" "a")
$1 = #t

Hypothesis:
guile's reader recognizes that the contents of both string literals
are the same and feeds the same string to the calling function. 

Check:
If that were the case, the two strings should not only be eqv? but
also eq?

scheme@(guile-user)> (eq? "a" "a")
$2 = #t

To see a different behaviour we need to avoid these literals and replace
them by values not built while reading but while executing the code:

scheme@(guile-user)> (equal? (list->string (list #\a)) (list->string (list #\a)))
$3 = #t

Now we compare two different string values which happen to end up with
identical content. And, behold: They are neither eqv? nor eq?.

scheme@(guile-user)> (eqv? (list->string (list #\a)) (list->string (list #\a)))
$4 = #f
scheme@(guile-user)> (eq? (list->string (list #\a)) (list->string (list #\a)))
$5 = #f


Now let's see if that is consistent with the standard:

According to r5rs, section 6.1 "Equivalence predicates":

        The eqv? procedure returns #t if:
        ...
        * obj1 and obj2 are pairs, vectors, or strings that denote
          the same locations in the store (section 3.4).

So we have learned 
* that guile's reader reuses "store locations" for
  strings of identical content 
* that eqv? is not the right predicate for content based string comparison

HTH
s.

"Zelphir Kaltstahl" <zelphirkaltstahl@posteo.de>, 20.09.2020, 15:09:

> And I've noticed something more about equality stuff in the context of
> tests:

> ~~~~
> (eqv? "a" "a")
> $3 = #t

> ;; but

(define char->>string
>   (λ (c)
>     (list->string
>      (list c))))

> (import
>   ;; unit tests
>   (srfi srfi-64))

> (test-begin "string-utils-test")

> (test-group
>  "char-to-string-test"

>  (test-eqv "char->string converts a character to a string"
>    "a"
>    (char->string #\a)))

> (test-end "string-utils-test")

> %%%% Starting test string-utils-test  (Writing full log to "string-utils-test.log")
> $2 = ("string-utils-test")
:19: FAIL char->>string converts a character to a string
> # of unexpected failures  1
> ~~~~

> So while (eqv? ...) gives the correct (?) result, the test procedure
> (test-eqv ...) which seems to indicate using (eqv? ...) via its name
> does not think of the two strings as equivalent.


> On 20.09.20 14:19, Zelphir Kaltstahl wrote:
>> Sorry, I misclicked "send" when I wanted to further edit my e-mail ...
>>
>> My Guile version is:
>>
>> ~~~~
>> (version)
>> $6 = "3.0.4"
>> ~~~~
>>
>> On 20.09.20 14:16, Zelphir Kaltstahl wrote:
>>> Hello Guile users,
>>>
>>> I just noticed something weird about eq?.
>>>
>>> My Guile version is:
>>>
>>>
>>> I get the different results, depending on whether I define some
>>> bindings in a let or using define:
>>>
>>> (In Emacs Geiser:)
>>>
>>> ~~~~
>>> (define x '(10 9))
>>> (define y '(10 9))
>>> (eq? x y)
>>> $2 = #f
>>>
>>> (let ([x '(10 9)]
>>>       [y '(10 9)])
>>>      (eq? x y))
>>> $3 = #t
>>> ~~~~
>>>
>>> Is this intentional or a bug?
>>>
>>> I first noticed something strange when writing the following code:
>>>
>>> ~~~~DEFINITION~~~~
>>> (define make-multiple-list-remover
>>>   (λ (equal-proc)
>>>     (λ (lst unwanted)
>>>       (let loop ([remaining-list lst])
>>>         (cond
>>>          [(null? remaining-list)
>>>           '()]
>>>          [(equal-proc (car remaining-list) unwanted)
>>>           (loop (cdr remaining-list))]
>>>          [else
>>>           (cons (car remaining-list)
>>>                 (loop (cdr remaining-list)))])))))
>>> ~~~~
>>>
>>> ~~~~TEST~~~~
>>> (let ([a '(9 10)]
>>>       [b '(9 10)])
>>>   (test-equal "make-multiple-list-remover-03"
>>>     `(1 2 (3) (4) ,a)
>>>     ((make-multiple-list-remover eq?)
>>>      `(a b (c) (d) ,a) b)))
>>> ~~~~
>>>
>>> I was wondering, why the test fails. I think (eq? ...) should not be
>>> able to see the equivalence of both lists a and b, just like when
>>> defined using (define ...).
>>>
>>> I can also run it in the REPL and see the difference:
>>>
>>> ~~~~
>>> (define a '(9 10))
>>> (define b '(9 10))
>>> ((make-multiple-list-remover eq?)
>>>  `(a b (c) (d) ,a) b)
>>> $4 = (a b (c) (d) (9 10))
>>>
>>> (let ([a '(9 10)]
>>>       [b '(9 10)])
>>>   ((make-multiple-list-remover eq?)
>>>    `(a b (c) (d) ,a) b))
>>> $5 = (a b (c) (d))
>>> ~~~~
>>>
>>> Somehow the bindings of let seem to be different from the bindings
>>> created using define. What about using define inside let?
>>>
>>> ~~~~
>>>
>>> ~~~~
>>> -- 
>>> repositories: https://notabug.org/ZelphirKaltstahl
>> Somehow the bindings of let seem to be different from the bindings
>> created using define. What about using define inside let?
>>
>> ~~~~
>> (let ([unrelated 'bla])
>>   (define a '(9 10))
>>   (define b '(9 10))
>>   ((make-multiple-list-remover eq?)
>>    `(a b (c) (d) ,a) b))
>> $7 = (a b (c) (d))
>> ~~~~
>>
>> So there the define usage also differs from when I use define on the top
>> level. Perhaps that is the difference? On which level the bindings are
>> defined?
>>
>> Regards,
>> Zelphir
>>


--
Stefan Schmiedl
EDV-Beratung Schmiedl, Berghangstr. 5, 93413 Cham
Büro: +49 (0) 9971 9966 989, Mobil: +49 (0) 160 9981 6278




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Surprising behavior of eq?
  2020-09-20 13:52     ` John Cowan
@ 2020-09-20 15:37       ` Zelphir Kaltstahl
  2020-09-20 16:18         ` tomas
  2020-09-20 17:05         ` John Cowan
  0 siblings, 2 replies; 13+ messages in thread
From: Zelphir Kaltstahl @ 2020-09-20 15:37 UTC (permalink / raw)
  To: John Cowan; +Cc: guile-user

Hello John, hello Stefan!

My previous understanding, it seems, was too shallow to explain the
behavior I saw. I think I misread
https://stackoverflow.com/a/17719745/1829329, where it says:

"This is where the eqv? predicate comes into picture. The eqv? is
exactly the same as the eq? predicate, except that it will always return
#t for same primitive values."

Of course SO is not a standard. Either it is simply wrong, or I
misunderstood "primitive values" in that phrase. I thought: "Ah strings
are a primitive value, so eqv? should work in all cases when comparing
strings." However, this has been debunked.

Anyway, your explanation makes sense to me and I learned something new:
Schemes differ in the way they "consolidate" literals.

I know Racket says it is not a Scheme any longer, but I still tried:

~~~~
Welcome to Racket v7.0.
> (let ((g "xyz") (h "xyz"))  (eqv? g h))
#t
> (define g "xyz")
> (define h "xyz")
> (eqv? g h)
#t
> 
~~~~

So it seems Racket consolidates literals from multiple top level
expressions.

While in GNU Guile:

~~~~
scheme@(guile-user)> (let ((g "xyz") (h "xyz"))  (eqv? g h))
$1 = #t
scheme@(guile-user)> (define g "xyz")
scheme@(guile-user)> (define h "xyz")
scheme@(guile-user)> (eqv? g h)
$2 = #f
scheme@(guile-user)>
~~~~

So there we have a difference in how literals are consolidated in GNU
Guile and Racket. Interesting!

Thank you for your explanation!


The only thing I do not quite understand yet is: What is the difference
between (eqv? ...) and (eq? ...) then? If (eqv? ...) is only #t if
things get consolidated in the same store place, would that not be the
same as pointer equivalence? Ah, OK, the difference is, that (eqv? ...)
works for numbers, regardless of whether they are consolidated, while
eq? does not recognize them as equivalent:

~~~~
(define a 3.0)
(define b 3.0)
(eq? a b)
$6 = #f

(eqv? a b)
$7 = #t
~~~~

So even if they are not consolidated (put in the same store), (eqv? ...)
has some internal logic to treat numbers differently, while (eq? ...)
does not do that.

In the Guile manual there is no example given, for which (eq? ...) and
(eqv? ...) give different results:
https://www.gnu.org/software/guile/manual/html_node/Equality.html. When
I try the example given for (eq? ...), I get the same results for (eqv?
...):

~~~~
(define x (vector 1 2 3))
(define y (vector 1 2 3))

(eq? x x)  ⇒ #t
(eq? x y)  ⇒ #f
(eqv? x x)  ⇒ #t
(eqv? x y)  ⇒ #f
~~~~


I also read Stefan's explanation, which seems to boil down to the same
thing: Same or different locations in the "store".

Also that (eqv? ...) is not the correct thing to use for strings.

Best regards,
Zelphir


On 9/20/20 3:52 PM, John Cowan wrote:
> What's happening is that you are trying to compare strings with an
> inappropriate predicate.  Eqv? tests for *identity* (being the same
> object), whereas equal? tests for *equality* (havng the same
> structure).  Therefore, (equal? "foo" "foo") will always return #t,
> and almost all the time that's the predicate you should use.
>
> The question then is: when are two strings identical and not merely
> equal?  In the general case, they are identical if they are the result
> of a single constructor.  Thus given:
>
> (define a (string #\a #\b #\c))
> (define b a)
>
> then (eqv? a b) => #t
>
> But given
>
> (define c (string #\a #\b #c))
> (define d (string #\a #\b #c))
>
> then (eqv? c  d) => #f
>
> Finally, we have to deal with string literals.  If there is more than
> one appearance of "xyz" in code, they are all equal, but are they
> identical?  The answer is that it is implementation-dependent.  A
> Scheme is free to consolidate all equal literals into the same
> object.  It looks like Guile does so when they both appear in the same
> top-level expression, but *not* in different top-level expressions. 
> So given
>
> (define e "xyz")
> (define f "xyz")
>
> then (eqv? e f) => #f
>
> But (let ((g "xyz") (h "xyz"))  (eqv? g h)) => #t, because Guile
> consolidates the two equal literals into a single object.  If you try
> this on Chibi Scheme, you'll get #f, because Chibi does *not*
> consolidate string literals.
>
> I hope that is helpful.
>
>
>
>
> On Sun, Sep 20, 2020 at 9:11 AM Zelphir Kaltstahl
> <zelphirkaltstahl@posteo.de <mailto:zelphirkaltstahl@posteo.de>> wrote:
>
>     And I've noticed something more about equality stuff in the context of
>     tests:
>
>     ~~~~
>     (eqv? "a" "a")
>     $3 = #t
>
>     ;; but
>
>     (define char->string
>       (λ (c)
>         (list->string
>          (list c))))
>
>     (import
>       ;; unit tests
>       (srfi srfi-64))
>
>     (test-begin "string-utils-test")
>
>     (test-group
>      "char-to-string-test"
>
>      (test-eqv "char->string converts a character to a string"
>        "a"
>        (char->string #\a)))
>
>     (test-end "string-utils-test")
>
>     %%%% Starting test string-utils-test  (Writing full log to
>     "string-utils-test.log")
>     $2 = ("string-utils-test")
>     :19: FAIL char->string converts a character to a string
>     # of unexpected failures  1
>     ~~~~
>
>     So while (eqv? ...) gives the correct (?) result, the test procedure
>     (test-eqv ...) which seems to indicate using (eqv? ...) via its name
>     does not think of the two strings as equivalent.
>
>
>     On 20.09.20 14:19, Zelphir Kaltstahl wrote:
>     > Sorry, I misclicked "send" when I wanted to further edit my
>     e-mail ...
>     >
>     > My Guile version is:
>     >
>     > ~~~~
>     > (version)
>     > $6 = "3.0.4"
>     > ~~~~
>     >
>     > On 20.09.20 14:16, Zelphir Kaltstahl wrote:
>     >> Hello Guile users,
>     >>
>     >> I just noticed something weird about eq?.
>     >>
>     >> My Guile version is:
>     >>
>     >>
>     >> I get the different results, depending on whether I define some
>     >> bindings in a let or using define:
>     >>
>     >> (In Emacs Geiser:)
>     >>
>     >> ~~~~
>     >> (define x '(10 9))
>     >> (define y '(10 9))
>     >> (eq? x y)
>     >> $2 = #f
>     >>
>     >> (let ([x '(10 9)]
>     >>       [y '(10 9)])
>     >>      (eq? x y))
>     >> $3 = #t
>     >> ~~~~
>     >>
>     >> Is this intentional or a bug?
>     >>
>     >> I first noticed something strange when writing the following code:
>     >>
>     >> ~~~~DEFINITION~~~~
>     >> (define make-multiple-list-remover
>     >>   (λ (equal-proc)
>     >>     (λ (lst unwanted)
>     >>       (let loop ([remaining-list lst])
>     >>         (cond
>     >>          [(null? remaining-list)
>     >>           '()]
>     >>          [(equal-proc (car remaining-list) unwanted)
>     >>           (loop (cdr remaining-list))]
>     >>          [else
>     >>           (cons (car remaining-list)
>     >>                 (loop (cdr remaining-list)))])))))
>     >> ~~~~
>     >>
>     >> ~~~~TEST~~~~
>     >> (let ([a '(9 10)]
>     >>       [b '(9 10)])
>     >>   (test-equal "make-multiple-list-remover-03"
>     >>     `(1 2 (3) (4) ,a)
>     >>     ((make-multiple-list-remover eq?)
>     >>      `(a b (c) (d) ,a) b)))
>     >> ~~~~
>     >>
>     >> I was wondering, why the test fails. I think (eq? ...) should
>     not be
>     >> able to see the equivalence of both lists a and b, just like when
>     >> defined using (define ...).
>     >>
>     >> I can also run it in the REPL and see the difference:
>     >>
>     >> ~~~~
>     >> (define a '(9 10))
>     >> (define b '(9 10))
>     >> ((make-multiple-list-remover eq?)
>     >>  `(a b (c) (d) ,a) b)
>     >> $4 = (a b (c) (d) (9 10))
>     >>
>     >> (let ([a '(9 10)]
>     >>       [b '(9 10)])
>     >>   ((make-multiple-list-remover eq?)
>     >>    `(a b (c) (d) ,a) b))
>     >> $5 = (a b (c) (d))
>     >> ~~~~
>     >>
>     >> Somehow the bindings of let seem to be different from the bindings
>     >> created using define. What about using define inside let?
>     >>
>     >> ~~~~
>     >>
>     >> ~~~~
>     >> --
>     >> repositories: https://notabug.org/ZelphirKaltstahl
>     > Somehow the bindings of let seem to be different from the bindings
>     > created using define. What about using define inside let?
>     >
>     > ~~~~
>     > (let ([unrelated 'bla])
>     >   (define a '(9 10))
>     >   (define b '(9 10))
>     >   ((make-multiple-list-remover eq?)
>     >    `(a b (c) (d) ,a) b))
>     > $7 = (a b (c) (d))
>     > ~~~~
>     >
>     > So there the define usage also differs from when I use define on
>     the top
>     > level. Perhaps that is the difference? On which level the
>     bindings are
>     > defined?
>     >
>     > Regards,
>     > Zelphir
>     >
>     -- 
>     repositories: https://notabug.org/ZelphirKaltstahl
>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Surprising behavior of eq?
  2020-09-20 13:57     ` Stefan Schmiedl
@ 2020-09-20 15:42       ` Zelphir Kaltstahl
  0 siblings, 0 replies; 13+ messages in thread
From: Zelphir Kaltstahl @ 2020-09-20 15:42 UTC (permalink / raw)
  To: Stefan Schmiedl; +Cc: guile-user

Hello Stefan!

Thank you for your explanation as well!

On 9/20/20 3:57 PM, Stefan Schmiedl wrote:
> Let's have a little shop talk with guile:
>
> $ guile-3.0
> GNU Guile 3.0.4-dirty
> scheme@(guile-user)> (eqv? "a" "a")
> $1 = #t
>
> Hypothesis:
> guile's reader recognizes that the contents of both string literals
> are the same and feeds the same string to the calling function. 
>
> Check:
> If that were the case, the two strings should not only be eqv? but
> also eq?
>
> scheme@(guile-user)> (eq? "a" "a")
> $2 = #t
>
> To see a different behaviour we need to avoid these literals and replace
> them by values not built while reading but while executing the code:
>
> scheme@(guile-user)> (equal? (list->string (list #\a)) (list->string (list #\a)))
> $3 = #t
>
> Now we compare two different string values which happen to end up with
> identical content. And, behold: They are neither eqv? nor eq?.
>
> scheme@(guile-user)> (eqv? (list->string (list #\a)) (list->string (list #\a)))
> $4 = #f
> scheme@(guile-user)> (eq? (list->string (list #\a)) (list->string (list #\a)))
> $5 = #f
>
>
> Now let's see if that is consistent with the standard:
>
> According to r5rs, section 6.1 "Equivalence predicates":
>
>         The eqv? procedure returns #t if:
>         ...
>         * obj1 and obj2 are pairs, vectors, or strings that denote
>           the same locations in the store (section 3.4).
>
> So we have learned 
> * that guile's reader reuses "store locations" for
>   strings of identical content 

I even learned a little bit about store locations and that a "store"
exist as a concept in the implementation of Scheme and GNU Guile ; )

> * that eqv? is not the right predicate for content based string comparison
I see! Thanks!
> HTH
> s.
>
> "Zelphir Kaltstahl" <zelphirkaltstahl@posteo.de>, 20.09.2020, 15:09:
>
>> And I've noticed something more about equality stuff in the context of
>> tests:
>>
>> ~~~~
>> (eqv? "a" "a")
>> $3 = #t
>>
>> ;; but
>>
>> (define char->>string
>>   (λ (c)
>>     (list->string
>>      (list c))))
>>
>> (import
>>   ;; unit tests
>>   (srfi srfi-64))
>> (test-begin "string-utils-test")
>>
>> (test-group
>>  "char-to-string-test"
>>
>> (test-eqv "char->string converts a character to a string"
>>   "a"
>>   (char->string #\a)))
>>
>> (test-end "string-utils-test")
>>
>> %%%% Starting test string-utils-test  (Writing full log to "string-utils-test.log")
>> $2 = ("string-utils-test")
>> :19: FAIL char->>string converts a character to a string
>> # of unexpected failures  1
>> ~~~~
>>
>> So while (eqv? ...) gives the correct (?) result, the test procedure
>> (test-eqv ...) which seems to indicate using (eqv? ...) via its name
>> does not think of the two strings as equivalent.
>>
>>
>> On 20.09.20 14:19, Zelphir Kaltstahl wrote:
>>> Sorry, I misclicked "send" when I wanted to further edit my e-mail ...
>>>
>>> My Guile version is:
>>>
>>> ~~~~
>>> (version)
>>> $6 = "3.0.4"
>>> ~~~~
>>>
>>> On 20.09.20 14:16, Zelphir Kaltstahl wrote:
>>>> Hello Guile users,
>>>>
>>>> I just noticed something weird about eq?.
>>>>
>>>> My Guile version is:
>>>>
>>>>
>>>> I get the different results, depending on whether I define some
>>>> bindings in a let or using define:
>>>>
>>>> (In Emacs Geiser:)
>>>>
>>>> ~~~~
>>>> (define x '(10 9))
>>>> (define y '(10 9))
>>>> (eq? x y)
>>>> $2 = #f
>>>>
>>>> (let ([x '(10 9)]
>>>>       [y '(10 9)])
>>>>      (eq? x y))
>>>> $3 = #t
>>>> ~~~~
>>>>
>>>> Is this intentional or a bug?
>>>>
>>>> I first noticed something strange when writing the following code:
>>>>
>>>> ~~~~DEFINITION~~~~
>>>> (define make-multiple-list-remover
>>>>   (λ (equal-proc)
>>>>     (λ (lst unwanted)
>>>>       (let loop ([remaining-list lst])
>>>>         (cond
>>>>          [(null? remaining-list)
>>>>           '()]
>>>>          [(equal-proc (car remaining-list) unwanted)
>>>>           (loop (cdr remaining-list))]
>>>>          [else
>>>>           (cons (car remaining-list)
>>>>                 (loop (cdr remaining-list)))])))))
>>>> ~~~~
>>>>
>>>> ~~~~TEST~~~~
>>>> (let ([a '(9 10)]
>>>>       [b '(9 10)])
>>>>   (test-equal "make-multiple-list-remover-03"
>>>>     `(1 2 (3) (4) ,a)
>>>>     ((make-multiple-list-remover eq?)
>>>>      `(a b (c) (d) ,a) b)))
>>>> ~~~~
>>>>
>>>> I was wondering, why the test fails. I think (eq? ...) should not be
>>>> able to see the equivalence of both lists a and b, just like when
>>>> defined using (define ...).
>>>>
>>>> I can also run it in the REPL and see the difference:
>>>>
>>>> ~~~~
>>>> (define a '(9 10))
>>>> (define b '(9 10))
>>>> ((make-multiple-list-remover eq?)
>>>>  `(a b (c) (d) ,a) b)
>>>> $4 = (a b (c) (d) (9 10))
>>>>
>>>> (let ([a '(9 10)]
>>>>       [b '(9 10)])
>>>>   ((make-multiple-list-remover eq?)
>>>>    `(a b (c) (d) ,a) b))
>>>> $5 = (a b (c) (d))
>>>> ~~~~
>>>>
>>>> Somehow the bindings of let seem to be different from the bindings
>>>> created using define. What about using define inside let?
>>>>
>>>> ~~~~
>>>>
>>>> ~~~~
>>>> -- 
>>>> repositories: https://notabug.org/ZelphirKaltstahl
>>> Somehow the bindings of let seem to be different from the bindings
>>> created using define. What about using define inside let?
>>>
>>> ~~~~
>>> (let ([unrelated 'bla])
>>>   (define a '(9 10))
>>>   (define b '(9 10))
>>>   ((make-multiple-list-remover eq?)
>>>    `(a b (c) (d) ,a) b))
>>> $7 = (a b (c) (d))
>>> ~~~~
>>>
>>> So there the define usage also differs from when I use define on the top
>>> level. Perhaps that is the difference? On which level the bindings are
>>> defined?
>>>
>>> Regards,
>>> Zelphir
>>>
>
> --
> Stefan Schmiedl
> EDV-Beratung Schmiedl, Berghangstr. 5, 93413 Cham
> Büro: +49 (0) 9971 9966 989, Mobil: +49 (0) 160 9981 6278  

Best regards,

Zelphir



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Surprising behavior of eq?
  2020-09-20 15:37       ` Zelphir Kaltstahl
@ 2020-09-20 16:18         ` tomas
  2020-09-20 17:05         ` John Cowan
  1 sibling, 0 replies; 13+ messages in thread
From: tomas @ 2020-09-20 16:18 UTC (permalink / raw)
  To: guile-user

[-- Attachment #1: Type: text/plain, Size: 957 bytes --]

On Sun, Sep 20, 2020 at 05:37:45PM +0200, Zelphir Kaltstahl wrote:
> Hello John, hello Stefan!

[...]

> Of course SO is not a standard. Either it is simply wrong, or I
> misunderstood "primitive values" in that phrase. I thought: "Ah strings
> are a primitive value, so eqv? should work in all cases when comparing
> strings." However, this has been debunked.

Strings are mutable in Guile, so probably not what's called "primitive
values":

  scheme@(guile-user)> (define str (string-copy "the quick brown fox"))
  scheme@(guile-user)> (string-set! str 6 #\a)
  scheme@(guile-user)> str
  $3 = "the quack brown fox"

But string literals bark at you if you try to mutate them (that's why
I slyly created that one with `string-copy'):

  scheme@(guile-user)> (define str "the quick brown fox")
  scheme@(guile-user)> (string-set! str 6 #\a)
  ice-9/boot-9.scm:1669:16: In procedure raise-exception:
  string is read-only: "the quick brown fox"

Cheers
 - t

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Surprising behavior of eq?
  2020-09-20 15:37       ` Zelphir Kaltstahl
  2020-09-20 16:18         ` tomas
@ 2020-09-20 17:05         ` John Cowan
  2020-09-20 20:51           ` Linus Björnstam
  1 sibling, 1 reply; 13+ messages in thread
From: John Cowan @ 2020-09-20 17:05 UTC (permalink / raw)
  To: Zelphir Kaltstahl; +Cc: guile-user

On Sun, Sep 20, 2020 at 11:37 AM Zelphir Kaltstahl <
zelphirkaltstahl@posteo.de> wrote:

> "This is where the eqv? predicate comes into picture. The eqv? is exactly
> the same as the eq? predicate, except that it will always return #t for
> same primitive values."
>
> Of course SO is not a standard. Either it is simply wrong, or I
> misunderstood "primitive values" in that phrase. I thought: "Ah strings are
> a primitive value, so eqv? should work in all cases when comparing
> strings." However, this has been debunked.
>
Strings tend to be treated as primitive values, but technically they are
compound values, just as much as vectors are.  And like vectors, you can
change the characters of a string without changing its identity.

> The only thing I do not quite understand yet is: What is the difference
> between (eqv? ...) and (eq? ...) then?
>
Efficiency only.

> If (eqv? ...) is only #t if things get consolidated in the same store
> place, would that not be the same as pointer equivalence?
>
Eq? is allowed to return #f on identical-looking characters and numbers
because they may or may not involve allocation.  Characters and fixnums
(integers up to about 2^60) are generally not allocated and eq? will return
#t on them, but larger integers and other types almost certainly will not:

(let ((i (* 48923498234892340 78902372789023)) (j (* 48923498234892340
78902372789023))) (eq? i j)) => #f

That's because the values of both i and j have to be allocated, and
therefore aren't represented by the same pointers.  But unlike strings, you
can't mutate the digits of an integer, so providing eqv? gives us an
abstract notion of identity.  The Scheme standards simplify the situation
by saying that eq? may return #f even when eqv? returns #t in the cases of
numbers and characters.

Because of these strange effects, I recommend (and this is just me) not
using eq? unless you can prove that you need the additional effi.

Note that (eq? 5 5.0) and (eqv? 5 5.0) are both #f, so if you want to
compare for numeric equality you should use (= 5 5.0) => #t.  The reason
they are not identical is that they behave differently:  (/ 5.0 2) => 2.5,
whereas (/ 5 2) => 5/2.

Here's an example that shows us that Guile does not consolidate number
literals, although it could:

(eqv? 3860180095872584138419938783820 3860180095872584138419938783820) =>#f

(In R7RS, procedures that are eqv? aren't necessarily eq? either, and in
R6RS even procedures that seem totally identical may be neither eq? nor
eqv?.  That's another story.)


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Surprising behavior of eq?
  2020-09-20 12:16 Surprising behavior of eq? Zelphir Kaltstahl
  2020-09-20 12:19 ` Zelphir Kaltstahl
@ 2020-09-20 17:26 ` Taylan Kammer
  1 sibling, 0 replies; 13+ messages in thread
From: Taylan Kammer @ 2020-09-20 17:26 UTC (permalink / raw)
  To: Zelphir Kaltstahl, Guile User

Others already mentioned 'equal?' as the correct predicate for testing 
structural content equality.  It works not only for strings but also 
arbitrarily nested structures, for instance:


   (define a (list 'a 9871239876234 "foo" (vector #\a #\b #\c)))

   (define b '(a 9871239876234 (string #\f #\o #\o) #(#\a #\b #\c)))

   (equal? a b) ;=> #t


   (define c "foo")

   (define d 1234)

   (equal? c d) ;=> #f  (obviously)


But if you know that two variables both reference a string object, 
there's a more appropriate predicate which explicitly tests for string 
equality: 'string=?'


   (define a "foo")

   (define b (string #\f #\o #\o))

   (string=? a b) ;=> #t


   (define c "foo")

   (define d 1234)

   (string=? c d) ;=> ERROR: wrong type argument 1234 passed to string=?


Using 'string=?' instead of 'equal?' has a few small advantages in this 
case:

- It makes it obvious to the reader of the code that two variables are 
both supposed to reference a string.

- If due to some strange mistake one or both of the variables end up 
referencing an object that is not a string, the 'string=?' call will 
immediately raise an exception, which is probably better than it just 
returning #f and letting the code continue to run, because the code will 
probably crash at a later point anyway due to the object with the wrong 
type.  (It's best for code to crash as soon as possible when an error 
like that happens, so you can find the root cause easily.)

- Lastly, 'string=?' probably has a tiny performance advantage, though 
that's probably not important here.  (If you experience performance 
issues with your code, you should benchmark it to see where the actual 
issue is.  I'm just mentioning this for completeness' sake.)


Of course if your variables may contain different types of values like 
lists and vectors and not just strings, you should indeed use 'equal?'.


- Taylan



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Surprising behavior of eq?
  2020-09-20 17:05         ` John Cowan
@ 2020-09-20 20:51           ` Linus Björnstam
  2020-09-20 21:42             ` John Cowan
  0 siblings, 1 reply; 13+ messages in thread
From: Linus Björnstam @ 2020-09-20 20:51 UTC (permalink / raw)
  To: John Cowan, Zelphir Kaltstahl; +Cc: guile-user

Just a quick note on guile: if you are testing equality against literals guile will optimize to the fastest kind. (equal? b 27) becomes (eq? b 27). This was added by Andy sometime since 3.0, after Aleix discovered that match was slower than cond-with-eqv? for chars, and that the difference was huge. I wrote an awful patch, which made Andy barf so he fixed it properly (no. He was very polite. If he barfed, he didn't mention it).

A simple optimization that shaved something like 15% of time spent in json->scm in guile-json on one of the files we used for benchmarking. It was converted to cond for the benefit of 2.2, though, but it is still a nice thing to be able to rely on when writing macros.

-- 
  Linus Björnstam

On Sun, 20 Sep 2020, at 19:05, John Cowan wrote:
> On Sun, Sep 20, 2020 at 11:37 AM Zelphir Kaltstahl <
> zelphirkaltstahl@posteo.de> wrote:
> 
> > "This is where the eqv? predicate comes into picture. The eqv? is exactly
> > the same as the eq? predicate, except that it will always return #t for
> > same primitive values."
> >
> > Of course SO is not a standard. Either it is simply wrong, or I
> > misunderstood "primitive values" in that phrase. I thought: "Ah strings are
> > a primitive value, so eqv? should work in all cases when comparing
> > strings." However, this has been debunked.
> >
> Strings tend to be treated as primitive values, but technically they are
> compound values, just as much as vectors are.  And like vectors, you can
> change the characters of a string without changing its identity.
> 
> > The only thing I do not quite understand yet is: What is the difference
> > between (eqv? ...) and (eq? ...) then?
> >
> Efficiency only.
> 
> > If (eqv? ...) is only #t if things get consolidated in the same store
> > place, would that not be the same as pointer equivalence?
> >
> Eq? is allowed to return #f on identical-looking characters and numbers
> because they may or may not involve allocation.  Characters and fixnums
> (integers up to about 2^60) are generally not allocated and eq? will return
> #t on them, but larger integers and other types almost certainly will not:
> 
> (let ((i (* 48923498234892340 78902372789023)) (j (* 48923498234892340
> 78902372789023))) (eq? i j)) => #f
> 
> That's because the values of both i and j have to be allocated, and
> therefore aren't represented by the same pointers.  But unlike strings, you
> can't mutate the digits of an integer, so providing eqv? gives us an
> abstract notion of identity.  The Scheme standards simplify the situation
> by saying that eq? may return #f even when eqv? returns #t in the cases of
> numbers and characters.
> 
> Because of these strange effects, I recommend (and this is just me) not
> using eq? unless you can prove that you need the additional effi.
> 
> Note that (eq? 5 5.0) and (eqv? 5 5.0) are both #f, so if you want to
> compare for numeric equality you should use (= 5 5.0) => #t.  The reason
> they are not identical is that they behave differently:  (/ 5.0 2) => 2.5,
> whereas (/ 5 2) => 5/2.
> 
> Here's an example that shows us that Guile does not consolidate number
> literals, although it could:
> 
> (eqv? 3860180095872584138419938783820 3860180095872584138419938783820) =>#f
> 
> (In R7RS, procedures that are eqv? aren't necessarily eq? either, and in
> R6RS even procedures that seem totally identical may be neither eq? nor
> eqv?.  That's another story.)
>



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Surprising behavior of eq?
  2020-09-20 20:51           ` Linus Björnstam
@ 2020-09-20 21:42             ` John Cowan
  0 siblings, 0 replies; 13+ messages in thread
From: John Cowan @ 2020-09-20 21:42 UTC (permalink / raw)
  To: Linus Björnstam; +Cc: guile-user

On Sun, Sep 20, 2020 at 4:52 PM Linus Björnstam <linus.internet@fastmail.se>
wrote:

Just a quick note on guile: if you are testing equality against literals
> guile will optimize to the fastest kind. (equal? b 27) becomes (eq? b 27).


Indeed, it's a Good Thing if equal? starts out with an eqv? test (which
should be inlined except for number comparisons) and eqv? starts out with
an eq? test (which also should be inlined).  If the more fine-grained test
returns #t, the more coarse-grained one can safely return #t also.  This is
independent of literals, and should IMO be done by every Scheme.


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2020-09-20 21:42 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-20 12:16 Surprising behavior of eq? Zelphir Kaltstahl
2020-09-20 12:19 ` Zelphir Kaltstahl
2020-09-20 12:58   ` Christopher Lemmer Webber
2020-09-20 13:09   ` Zelphir Kaltstahl
2020-09-20 13:52     ` John Cowan
2020-09-20 15:37       ` Zelphir Kaltstahl
2020-09-20 16:18         ` tomas
2020-09-20 17:05         ` John Cowan
2020-09-20 20:51           ` Linus Björnstam
2020-09-20 21:42             ` John Cowan
2020-09-20 13:57     ` Stefan Schmiedl
2020-09-20 15:42       ` Zelphir Kaltstahl
2020-09-20 17:26 ` Taylan Kammer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).