greghendershott / rackjure

Provide a few Clojure-inspired ideas in Racket. Where Racket and Clojure conflict, prefer Racket.
BSD 2-Clause "Simplified" License
236 stars 17 forks source link

Add egal.rkt. #21

Closed technomancy closed 10 years ago

technomancy commented 10 years ago

I'm not 100% sure this covers all the bases, but I think it's a good start.

greghendershott commented 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!

soegaard commented 10 years ago

This might be a silly question, but when does egal? and equal? return different values? They look very similar to me.

greghendershott commented 10 years ago

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?

technomancy commented 10 years ago

@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
greghendershott commented 10 years ago

@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...
technomancy commented 10 years ago

Yep; looks like we aren't currently covering pairs properly; I assumed sequence? would cover it. Nice catch.

greghendershott commented 10 years ago

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

technomancy commented 10 years ago

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.