Fork me on GitHub

If this is already documented/explained somewhere that you know of, I would appreciate a reference. Is there a straightforward reason why defrecord records are not clojure.core/= to maps with the same key/value pairs?

Alex Miller (Clojure team)01:05:56

Because they have a type


I guess meaning "a type with a name chosen by the defrecord creator, intended to be used explicitly by that name for later purposes, like which protocols it interacts with?" I'm not trying to be pedantic here, but may lapse that direction -- PersistentHashMap and PersistentArrayMap have a type in the JVM, too, but instances of those types with the same key/val pairs are = to each other. I know those type names are typically not intended to be cared about by most users of maps.


If you have two different defrecord's, but each with the same set of key/val pairs. Are they then equal per clojure.core/=? My own thoughts are, as the developer has given a defrecord a specific type that should infer meaning upon it.

Alex Miller (Clojure team)02:05:44

no, they’re not equal

Alex Miller (Clojure team)02:05:11

with maps, the “java type” is not part of the comparison. with records, it is. This is (one of) the big features added by records - a named type.


My question was more rhetorical... two defrecords would not be considered equal, so logically a defrecord and map should not be as well.

Alex Miller (Clojure team)02:05:58

in practice, I don’t know that I’ve ever run into this as an issue, and I’ve used defrecords a lot


@the2bears And my question could be applied just as well to two defrecord instances with different types, but same key/value pairs, being considered =. I'm not saying I think anything should change, just looking for the rationale.


Clojure goes out of its way to make things with different types clojure.core/= to each other, e.g. Java Integer/Long/BigInteger/Short/Byte with the same numeric values. Also lists/vectors/queues, both mutable and immutable.


Hmmm... for me with two defrecord instances it's more cut and dry. A developer has named them differently, typed differently, on purpose. They should not be treated as equal. But I can see your point re:the examples you listed.


It's conceivable a multimethod would treat them differently so then the "same" input would yield different results to the same function


Especially when the defrecord is converted to a map through removal of a declared key.


*converted in the immutable, functional way 🙂


A multimethod on type would break a = b => f(a) = f(b)


clojure.core/conj breaks a = b => f(a) = f(b) by design. I'm not saying the same rules should be applied to both, just that a=b => f(a)=f(b) is not a golden rule that Clojure claims to follow


Ah. What's an example of that


conj on list vs. vector


(= [1 2 3] '(1 2 3)) but not (= (conj [1 2 3] 4) (conj '(1 2 3) 4))


Interesting discussion


I see your point now


Other functions f that do not follow that rule, I am pretty sure by design, not accident: meta seq occasionally hash


So I think the clearest rationale for defrecord is that by design, you use it when you want unique new types to be created, by name, and those names are important for taking advantage of the host's fast first-arg method dispatch capability for protocols, and because you want the ability for different types to be treated differently in those protocols (and also multimethods).


That pretty quickly leads to desiring them to be not clojure.core/= to each other.


I could be butchering all kinds of subtleties there, of course, but at least it gives me a bit of an "ah hah".


@dpsutton I wrote a couple of articles on deep dark corners of clojure.core/= and how it relates to hash consistency and the property a=b => f(a)=f(b), which I will link here in case you find anything further interesting there: Clojure equality: "Referential transparency", which I'm not sure any more that any two people use to mean the same thing when they say it, so the article is probably misnamed 🙂


I have been revisiting and thinking about those articles again recently, and realized I had never mentioned defrecord behavior in the equality article. Looking to update those, so feel free to comment on anything you find wrong or confusing.

Alex Miller (Clojure team)02:05:48

I’ve actually substantially rewritten your equality one with the goal to post it as a guide

Alex Miller (Clojure team)02:05:05

kind of got stuck in it though


Cool. I realize that the version I wrote perhaps doesn't exactly "accentuate the positive" but maybe focuses too much on the weird corners.

Alex Miller (Clojure team)02:05:46

well that was definitely part of it


I was thinking of an audience who wants to know where all the potential land mines are 🙂

Alex Miller (Clojure team)02:05:14

wanted to focus it on intent and how things work

Alex Miller (Clojure team)02:05:20

yes, and it’s useful for that

Alex Miller (Clojure team)02:05:34

but that’s different than a guide


which thankfully are far and few between


I guess you would prefer not to publish your incompletely edited rewrite of the equality article?


Thanks for the links. Those will go with coffee tomorrow morning

Alex Miller (Clojure team)02:05:01

if you want it in it’s half-broken state, happy to send it to you

Alex Miller (Clojure team)02:05:10

well maybe happy, haven’t looked at it in a long while


Oh I was talking about Andy's but I'd love to read yours if you're willing to share

Alex Miller (Clojure team)02:05:55

I sent it to Andy, sorry

Alex Miller (Clojure team)03:05:10

prob not too good to share


Yeah. Misread. Mobile has smaller core viewport :)


Oh, and related to Baker's egal operator, I read another paper recently where they called egal "always equal", versus something like Java's .equals being called "equals now". I liked the nice short names that seem to call out a critical difference.


And even the name "equals now" is really maybe only a good one in a single-threaded language, since multi-threaded turns that into "who knows?" 🙂


That paper is linked here, in case anyone is curious: The authors of the paper seem to be designing a programming language, and even given all of their knowledge of the issue, including Baker's egal, they seem to have chosen in their design something closer to Java's equals.


as clojure (and the egal paper building on common lisp) shows, you can add egal, but adding a way to check for pointer equality if you don't build one in seems like it would be impossible


I never even thought to consider that while learning Clojure and reading the egal paper, given that checking for pointer equality is kind of a built-in in just about any programming language I've ever learned. What caused you to think of that?


does erlang or basic haskell have pointer equality?


haskell doesn't (modulo weird extensions i guess)


dunno about erlang