Fork me on GitHub
#clojure-dev
<
2018-05-05
>
andy.fingerhut01:05:41

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?

alexmiller01:05:56

Because they have a type

andy.fingerhut02:05:15

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.

the2bears02:05:21

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.

alexmiller02:05:44

no, they’re not equal

alexmiller02: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.

the2bears02:05:20

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

alexmiller02: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

andy.fingerhut02:05:42

@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.

andy.fingerhut02:05:29

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.

the2bears02:05:42

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.

dpsutton02:05:40

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

the2bears02:05:43

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

the2bears02:05:38

*converted in the immutable, functional way 🙂

dpsutton02:05:33

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

andy.fingerhut02:05:21

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

dpsutton02:05:38

Ah. What's an example of that

the2bears02:05:15

conj on list vs. vector

andy.fingerhut02:05:28

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

the2bears02:05:54

Interesting discussion

dpsutton02:05:01

I see your point now

andy.fingerhut02:05:08

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

andy.fingerhut02:05:46

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).

andy.fingerhut02:05:17

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

andy.fingerhut02:05:08

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

andy.fingerhut02:05:49

@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: https://github.com/jafingerhut/thalia/blob/master/doc/other-topics/equality.md "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 🙂 https://github.com/jafingerhut/thalia/blob/master/doc/other-topics/referential-transparency.md

andy.fingerhut02:05:31

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.

alexmiller02:05:48

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

alexmiller02:05:05

kind of got stuck in it though

andy.fingerhut02:05:27

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

alexmiller02:05:46

well that was definitely part of it

andy.fingerhut02:05:10

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

alexmiller02:05:14

wanted to focus it on intent and how things work

alexmiller02:05:20

yes, and it’s useful for that

alexmiller02:05:34

but that’s different than a guide

andy.fingerhut02:05:36

which thankfully are far and few between

andy.fingerhut02:05:53

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

dpsutton02:05:14

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

alexmiller02:05:01

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

alexmiller02:05:10

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

dpsutton02:05:26

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

alexmiller02:05:27

sent on email

alexmiller02:05:55

I sent it to Andy, sorry

alexmiller03:05:10

prob not too good to share

dpsutton03:05:07

Yeah. Misread. Mobile has smaller core viewport :)

andy.fingerhut03:05:07

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.

andy.fingerhut03:05:25

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?" 🙂

andy.fingerhut03:05:42

That paper is linked here, in case anyone is curious: http://lambda-the-ultimate.org/node/5509 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.

hiredman03:05:18

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

andy.fingerhut08:05:23

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?

gfredericks13:05:07

does erlang or basic haskell have pointer equality?

bronsa13:05:05

haskell doesn't (modulo weird extensions i guess)

bronsa13:05:08

dunno about erlang