Closed technomancy closed 10 years ago
Thanks! I'd like to see some unit tests. I think I'll go ahead and merge this, then try to add some myself -- partly as an exercise in making sure I understand the intent in a variety of cases. If anything surprises me, I'll ask.
Even if it's somewhat still "in progress", I think it's fine just to merge this to master as "experimental". Because it's neither documented nor re-provided by main.rkt. I think that's sufficient (as opposed to making a topic branch for it).
Thanks again!
This might be a silly question, but when does egal? and equal? return different values? They look very similar to me.
In other words I think you're asking is (egal? egal? equal?)
#t
-- i.e. are they "operationally equivalent"? ;)
On #racket last night I had a similar first reaction. @technomancy replied, because immutability (my weak paraphrase).
However I'm trying to write some units tests now, and one is for:
(egal? "foo" (string->immutable-string "foo"))
;; => #t
My expected result was #f
, so, either I'm misunderstanding or that needs to be fixed?
@soegaard The difference is that egal?
is referentially transparent--equal?
simply operates on the current value of a data structure without taking into account the fact that it could change at any instant. In contrast egal?
properly distinguishes between stable values and mutable "places" where modification can occur and uses object identity for the latter. The Baker paper in the comments has a much better explanation, of course: http://home.pipeline.com/~hbaker1/ObjectIdentity.html
Some more detailed documentation would certainly be helpful though.
@greghendershott You get #t
from that because "foo"
is already immutable. Comparing two equal?
mutable strings will return false:
(egal? (string-join '("hi")) (string-join '("hi"))) ; -> #f
@technomancy Although I'm still working on them, do you think these tests look like they're on the right track so far? See especially the ones toward the end, after ;; sequences
.
(module* test racket
(require (submod "..")
rackunit)
(define-syntax (== stx)
(syntax-case stx ()
[(_ x y) (syntax/loc stx
(check-true (egal? x y)))]))
(define-syntax (!= stx)
(syntax-case stx ()
[(_ x y) (syntax/loc stx
(check-false (egal? x y)))]))
(== #t #t)
(!= #t #f)
(== 0 0)
(!= 0 1)
(== #\a #\a)
(!= #\a #\b)
(== 'a 'a)
(!= 'a 'b)
(let ([a (box 'a)]
[b (box 'b)])
(== a a)
(!= a b))
(== #rx"a" #rx"a")
(!= #rx"a" #rx"b")
(== #px"a" #px"a")
(!= #px"a" #px"b")
(== (void) (void))
;;
;; sequences
;;
;; Although "string" literals are immutable, `string` isn't
(== "a" "a")
(!= "a" "b")
(!= (string #\a) (string #\a))
(== (string->immutable-string (string #\a))
(string->immutable-string (string #\a)))
;; Although #"bytes" literals are immutable, `bytes` isn't
(== #"a" #"a")
(!= #"a" #"b")
(!= (bytes 0) (bytes 0))
(== (bytes->immutable-bytes (bytes 0))
(bytes->immutable-bytes (bytes 0)))
;; Although #(0) literals are immutable (as is obviously
;; `vector-immutable`), `vector` isn't.
(== #(0) #(0))
(!= #(0) #(1))
(!= (vector 0) (vector 0))
(== (vector-immutable 0) (vector-immutable 0))
;;; and so on...
Yep; looks like we aren't currently covering pairs properly; I assumed sequence?
would cover it. Nice catch.
Here's what I came up with in terms of tests and a fix for pair. I made a branch so you could take a look and comment, if you want: https://github.com/greghendershott/rackjure/pull/22
It's important that the else
clause use eq?
. False negatives around operational equivalence can't be avoided (because actually calculating operational equivalence of procedures involves solving the halting problem) but false positives can and should be avoided.
I'm not 100% sure this covers all the bases, but I think it's a good start.