Skip to content

Instantly share code, notes, and snippets.

@metametadata
Created June 16, 2019 20:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save metametadata/2936244374fbd11d3f62b62490591b1b to your computer and use it in GitHub Desktop.
Save metametadata/2936244374fbd11d3f62b62490591b1b to your computer and use it in GitHub Desktop.
Multimethod helpers
(ns multi-plus.core)
(defn multimethod-map
"Converts multimethod into a map. :default dispatch value will be omitted.
Expected multimethod signature: (defmulti mm (fn [v] v)). It should be pure."
[mm]
(into {}
(for [[dispatch-value method] (methods mm)
:when (not= dispatch-value :default)]
[dispatch-value (method dispatch-value)])))
(defmacro with-method
"Temporarily adds multimethods for multifn. dispatch-map = {dispatch-val method}."
[multifn dispatch-map & body]
`(let [; Eager "evaluation" of arg is needed because it may contain side effects.
; E.g. for case when `(f/fake ...)` is passed in, clj-fakes self-tests will fail more times than expected.
evaluated-dispatch-map# ~dispatch-map]
; Race condition detection
(doseq [dispatch-val# (keys evaluated-dispatch-map#)]
(assert (not (contains? (methods ~multifn) dispatch-val#))
(str "with-method cannot add a method because there's already a method defined for dispatch-val "
(pr-str dispatch-val#))))
(try
(doseq [[dispatch-val# method#] evaluated-dispatch-map#]
(let [evaluated-method# method#]
(defmethod ~multifn dispatch-val#
[& args#]
(apply evaluated-method# args#))))
~@body
(finally
(doseq [dispatch-val# (keys evaluated-dispatch-map#)]
(remove-method ~multifn dispatch-val#))))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment