Skip to content

Instantly share code, notes, and snippets.

@bsless
Last active November 16, 2021 19:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bsless/50f3c673d11cf8ef18161a9138f55320 to your computer and use it in GitHub Desktop.
Save bsless/50f3c673d11cf8ef18161a9138f55320 to your computer and use it in GitHub Desktop.
What if multimethods were implemented with closures and not dispatch tables?
(defprotocol IDoubleDispatch
(-add [this k f])
(-remove [this k]))
(defmulti ->= type)
(defmethod ->= String [^String x] #(.equals x %))
(defmethod ->= clojure.lang.Keyword [^clojure.lang.Keyword x] #(.equals x %))
(defmethod ->= clojure.lang.Symbol [^clojure.lang.Symbol x] #(.equals x %))
(defmethod ->= Long [^long x]
#(if (int? %) (= x (unchecked-long %)) false))
(defmethod ->= Integer [x]
(let [x (long x)]
#(if (int? %) (= x (unchecked-long %)) false)))
(defmethod ->= Boolean [x] (if (true? x) #(.equals Boolean/TRUE %) #(.equals Boolean/FALSE %)))
(defmethod ->= :default [x] #(= x %))
(defn on-failure
[& args]
(throw (ex-info "No dispatch found" {:args args})))
(defn compile-mapping
[mapping]
(let [default (or (:default mapping) on-failure)]
(reduce
(fn [f* [k f]]
(let [=* (->= k)]
(fn
([x a] (if (=* x) (f a) (f* x a)))
([x a b] (if (=* x) (f a b) (f* x a b)))
([x a b c] (if (=* x) (f a b c) (f* x a b c)))
([x a b c d] (if (=* x) (f a b c d) (f* x a b c d)))
)))
default
(dissoc mapping :default))))
(deftype Magical [dispatch-fn mapping ^:unsynchronized-mutable f*]
clojure.lang.IFn
(invoke [this a] (f* (dispatch-fn a) a))
(invoke [this a b] (f* (dispatch-fn a b) a b))
(invoke [this a b c] (f* (dispatch-fn a b c) a b c))
IDoubleDispatch
(-add [_ k f] (set! f* (compile-mapping (swap! mapping assoc k f))))
(-remove [_ k] (set! f* (compile-mapping (swap! mapping dissoc k)))))
(defmacro defmulti*
[name dispatch]
`(def ~name (Magical. ~dispatch (atom {}) (compile-mapping {}))))
(defmacro defmethod*
[name dispatch-val & fn-tail]
`(-add ~name ~dispatch-val (fn ~@fn-tail)))
(defmulti* wizardry8 even?)
(defmethod* wizardry8 true [_] "even!")
(defmethod* wizardry8 false [_] "odd!")
(wizardry8 8)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment