Equality is a Problem

The following:

(= [0 1 2 3 4] '(0 1 2 3 4))

yields true. As does:

(= [0 1 2 3 4] '(0 1 2 3 4) (range 5) (map identity [0 1 2 3 4]))

and so on.

Is there any way at all to turn this behaviour “off”, that is, so that these expressions will yield false?

I am asking specifically for ClojureScript, but a general solution, workaround or whatever which works for Clojure/Script would be welcome. I have checked the source code and there doesn’t appear to be anything, so I suspect there is no way, but I’m sure I’m not the first person to ask this, and it would be a highly useful feature to at least be able to switch temporarily, e.g. via a binding call.

define your own = then, and use it in place of clojure.core/= (or cljs.core/=). It can have the binding you are interested in as part of the function definition (some dynvar co-located in the namespace). You also need to define your own equality semantics so that structural/value equality isn’t a thing (specifically equality between things that implement IPersistentCollection).

I think changing something as fundamental as the language notion of equality with a dynamic var is asking for problems and setting up consumers of the code for a lot of fun surprises to debug. I would probably stick with some bespoke functions that narrow down the specific equality deviations and limit the call sites to those functions so it is exceedingly obvious to trace behavior back to the source.

This is not something I have personally run across or seen asked for.

Can you explain what problem this causes?

I’ve been using Clojure in production for over a decade and this value-based equality has been very helpful in writing simpler code. If you want to constrain equality to the same type as well, you can always explicitly add that in the cases where you need that.

I had a similar thought but then realized that I could use identical? in my particular scenario, which has the advantage of both doing exactly what I want it to and saving me a lot of effort coding up a dedicated =. FWIW, I don’t think adding a binding which you consciously have to use will affect the core language in any way since unless you set it, everything will work exactly as it currently does. It would be a useful escape hatch for anyone who wants to employ it in a given scenario.

Sure: I’m building a reactive front-end library which reacts by checking if an old value has changed and then updates the UI accordingly. Since I am building a code editor using that, I need to be able to render an appropriate widget which will render either a vector or a list (or equivalent seq.) Using the core = doesn’t allow me to do that.

Okay, sounds like identical? first and falling back to type/equality would be the right choice then – and that’s what other reactive cljs libraries do I believe (and why they can get better performance than React.js, because they can rely on shared immutable data being identical).