Functions are black boxes and determining when two functions are equivalent is undecidable. That’s why:
(= (fn [x] x) (fn [x] x)) is false (a clever compiler could sove this naive specific case but not all).
However from time to time I’d like functions constructed the same way be considered equals. For example
(= (comp inc inc) (comp inc inc)) could be true and it wouldn’t necessitate a smart enough compiler or memoization.
It’s not without precedent: keywords, symbols, maps, sets and vectors are functions and also have value semantics:
(= (hash-set 1) (hash-set 1)) is true!
In indexed-set I use maps whose values are functions, sometimes functions returned by higher-order functions and the user having no reference to the function returned by the hof can’t look it up in the map — if she calls the hof agin with the same arguments she gets another function which is not equal to the first one. This means that I should always let the user call the hof by herself and keep the result around. It may be ok for a low-level API but not for more user-friendly functions.
Helpfully, records have value semantics, can implement Java interfaces and do not already implement
clojure.lang.IFn and that’s what I have done:
(defrecord Op-By [key f init] clojure.lang.IFn (invoke [this m v] (let [k (key v)] (assoc m k (f (m k init) v))))) (defn- op-by [key f init] (Op-By. key f init))
(defn- op-by [key f init] (fn [m v]) (let [k (key v)] (assoc m k (f (m k init) v))))
I’m still not happy with that I’d like something more fluid (closer to fn, maybe built on reify but ideally a metadata flag on
(^:valued fn [m v] ...)) ; I don’t think the type name is valueable, I’m on the fence concerning features I get for free from