non-equality (=) of records nuance

86 views
Skip to first unread message

Greg D

unread,
Mar 18, 2014, 4:40:08 PM3/18/14
to clo...@googlegroups.com
Greetings,

I'm confused by the failure of 2 record instances to compare as equal, only when generated by a protocol method and having extra fields.

The code below shows this, with uninformative REPL responses snipped. The attached file has similar code as a clojure.test.

Would somebody please help me understand the underlying mechanism. I'll need to develop a workaround to avoid confounding library users.

Thanks, Greg

user=> ;;; Set up protocol and records
user=> (defprotocol FooBar (clone [this]) (switch [this]))
user=> (defrecord Foo [])
user=> (defrecord Bar [])
user=> (extend-type Foo FooBar (clone [this] (map->Foo this)) (switch [this] (map->Bar this)))
user=> (extend-type Bar FooBar (clone [this] (map->Bar this)) (switch [this] (map->Foo this)))

user=> ;;; Create 4 (.equal) instances of Bar, no fields; all works as expected
user=> (def foo0 (->Foo))
user=> (def bar0 (->Bar))
user=> (def bar1 (->Bar))
user=> (def bar-switch-foo0 (switch foo0))
user=> (def bar-clone-bar0 (clone bar0))
user=> bar0
#user.Bar{}
user=> bar1
#user.Bar{}
user=> bar-switch-foo0
#user.Bar{}
user=> bar-clone-bar0
#user.Bar{}
user=> (= bar0 bar1)            ; gets expected true
true
user=> (= bar0 bar-switch-foo0) ; gets expected true
true
user=> (= bar0 bar-clone-bar0)  ; gets expected true
true

user=> ;;; Create 4 (.equal) instances of Bar with an added field; unexpected results
user=> (def x-foo0 (map->Foo {:field "xtra"}))
user=> (def x-bar0 (map->Bar {:field "xtra"}))
user=> (def x-bar1 (map->Bar {:field "xtra"}))
user=> (def bar-switch-x-foo0 (switch x-foo0))
user=> (def bar-clone-x-bar0 (clone x-bar0))
user=> x-bar0
#user.Bar{:field "xtra"}
user=> x-bar1
#user.Bar{:field "xtra"}
user=> bar-switch-x-foo0
#user.Bar{:field "xtra"}
user=> bar-clone-x-bar0
#user.Bar{:field "xtra"}
user=> (= x-bar0 x-bar1)                            ; gets expected true
true
user=> (= x-bar0 bar-switch-x-foo0)                 ; gets UNEXPECTED
false
user=> (= x-bar0 bar-clone-x-bar0)                  ; gets UNEXPECTED
false
user=> (= bar-switch-x-foo0 bar-clone-x-bar0)       ; gets UNEXPECTED
false
user=> (.equals x-bar0 bar-switch-x-foo0)           ; .equals is true
true
user=> (.equals x-bar0 bar-clone-x-bar0)            ; .equals is true
true
user=> (.equals bar-switch-x-foo0 bar-clone-x-bar0) ; .equals is true
true

record_instance_equality_test.clj

Alex Miller

unread,
Mar 18, 2014, 8:47:46 PM3/18/14
to clo...@googlegroups.com
What Clojure version are you on?

Nicola Mometto

unread,
Mar 18, 2014, 9:06:05 PM3/18/14
to clo...@googlegroups.com

Here's the bug:

user> (defrecord a [])
user.a
user> (defrecord b [])
user.b
user> (.__extmap (map->b (map->a {:a 1})))
#user.a{:a 1}

Alex Miller

unread,
Mar 18, 2014, 9:33:19 PM3/18/14
to clo...@googlegroups.com
Yeah that looks bad.

Nicola Mometto

unread,
Mar 18, 2014, 9:45:25 PM3/18/14
to clo...@googlegroups.com

I added a patch+tests here http://dev.clojure.org/jira/browse/CLJ-1388

Alex Miller writes:

> Yeah that looks bad.

Greg D

unread,
Mar 18, 2014, 10:27:04 PM3/18/14
to clo...@googlegroups.com
I'm on 1.5.1

I have a workaround:
  • instead of
    • (map->Foo this)
  • use
    •  (map->Foo (into {} this))
Reply all
Reply to author
Forward
0 new messages