From 257cb483e31c8e79545be19c6aa5a5fddaefb049 Mon Sep 17 00:00:00 2001 From: Abhinav Omprakash Date: Tue, 18 Oct 2022 09:10:16 +0530 Subject: [PATCH] Refactor Move Describable to methodical.interface --- src/methodical/core.clj | 7 +- src/methodical/impl/cache/simple.clj | 49 +++++--- src/methodical/impl/cache/watching.clj | 3 +- src/methodical/impl/combo/clojure.clj | 5 +- src/methodical/impl/combo/clos.clj | 5 +- src/methodical/impl/combo/operator.clj | 5 +- src/methodical/impl/combo/threaded.clj | 5 +- src/methodical/impl/dispatcher/everything.clj | 3 +- .../impl/dispatcher/multi_default.clj | 3 +- src/methodical/impl/dispatcher/standard.clj | 3 +- src/methodical/impl/method_table/clojure.clj | 64 ++++++++--- src/methodical/impl/method_table/common.clj | 4 +- src/methodical/impl/method_table/standard.clj | 5 +- src/methodical/impl/multifn/cached.clj | 7 +- src/methodical/impl/multifn/standard.clj | 104 +++++++++++------ src/methodical/impl/standard.clj | 5 +- src/methodical/interface.clj | 107 +++++++++++++----- src/methodical/util.clj | 7 +- src/methodical/util/describe.clj | 16 --- test/methodical/util/describe_test.clj | 4 +- 20 files changed, 258 insertions(+), 153 deletions(-) delete mode 100644 src/methodical/util/describe.clj diff --git a/src/methodical/core.clj b/src/methodical/core.clj index e0a140c..addb5f2 100644 --- a/src/methodical/core.clj +++ b/src/methodical/core.clj @@ -6,7 +6,6 @@ methodical.interface methodical.macros methodical.util - methodical.util.describe methodical.util.dispatch methodical.util.trace [potemkin :as p])) @@ -16,7 +15,6 @@ methodical.impl/keep-me methodical.interface/keep-me methodical.macros/keep-me - methodical.util.describe/keep-me methodical.util.dispatch/keep-me methodical.util.trace/keep-me methodical.util/keep-me) @@ -48,7 +46,8 @@ with-dispatcher method-table with-method-table - effective-method] + effective-method + describe] [methodical.impl ;; method combinations @@ -125,8 +124,6 @@ unprefer-method! with-prefers!] - [methodical.util.describe - describe] [methodical.util.dispatch dispatch-on-first-arg diff --git a/src/methodical/impl/cache/simple.clj b/src/methodical/impl/cache/simple.clj index 7b5ec61..684db5b 100644 --- a/src/methodical/impl/cache/simple.clj +++ b/src/methodical/impl/cache/simple.clj @@ -3,41 +3,62 @@ offers no facilities to deduplicate identical methods for the same dispatch value. This behaves similarly to the caching mechanism in vanilla Clojure." (:require - [clojure.core.protocols :as clojure.protocols] - [methodical.interface] - [methodical.util.describe :as describe] - [pretty.core :as pretty]) + [clojure.core.protocols :as clojure.protocols] + [methodical.interface :as i] + [pretty.core :as pretty]) (:import - (methodical.interface Cache))) + (methodical.interface + Cache))) + (set! *warn-on-reflection* true) (comment methodical.interface/keep-me) -(deftype SimpleCache [atomm] + +(deftype SimpleCache + [atomm] + pretty/PrettyPrintable - (pretty [_] + + (pretty + [_] '(simple-cache)) + Cache - (cached-method [_ dispatch-value] + + (cached-method + [_ dispatch-value] (get @atomm dispatch-value)) - (cache-method! [_ dispatch-value method] + + (cache-method! + [_ dispatch-value method] (swap! atomm assoc dispatch-value method)) - (clear-cache! [this] + + (clear-cache! + [this] (reset! atomm {}) this) - (empty-copy [_] + + (empty-copy + [_] (SimpleCache. (atom {}))) + clojure.protocols/Datafiable - (datafy [this] + + (datafy + [this] {:class (class this) :cache @atomm}) - describe/Describable - (describe [this] + + i/Describable + + (describe + [this] (format "It caches methods using a [[%s]]." (.getCanonicalName (class this))))) diff --git a/src/methodical/impl/cache/watching.clj b/src/methodical/impl/cache/watching.clj index 7768ee2..28ab533 100644 --- a/src/methodical/impl/cache/watching.clj +++ b/src/methodical/impl/cache/watching.clj @@ -15,7 +15,6 @@ [clojure.core.protocols :as clojure.protocols] [clojure.datafy :as datafy] [methodical.interface :as i] - [methodical.util.describe :as describe] [pretty.core :as pretty]) (:import (java.lang.ref WeakReference) @@ -55,7 +54,7 @@ :cache (datafy/datafy cache) :refs refs}) - describe/Describable + i/Describable (describe [this] (format "It caches methods using a [[%s]]." (.getCanonicalName (class this))))) diff --git a/src/methodical/impl/combo/clojure.clj b/src/methodical/impl/combo/clojure.clj index 08df3ed..0365dbb 100644 --- a/src/methodical/impl/combo/clojure.clj +++ b/src/methodical/impl/combo/clojure.clj @@ -3,8 +3,7 @@ not at all. Like vanilla Clojure multimethods, this method combination only supports primary methods." (:require [clojure.core.protocols :as clojure.protocols] - [methodical.interface] - [methodical.util.describe :as describe] + [methodical.interface :as i] [pretty.core :as pretty]) (:import (methodical.interface MethodCombination))) @@ -38,6 +37,6 @@ (datafy [this] {:class (class this)}) - describe/Describable + i/Describable (describe [this] (format "It uses the method combination [[%s]]." (.getCanonicalName (class this))))) diff --git a/src/methodical/impl/combo/clos.clj b/src/methodical/impl/combo/clos.clj index aa8c247..49cd641 100644 --- a/src/methodical/impl/combo/clos.clj +++ b/src/methodical/impl/combo/clos.clj @@ -6,8 +6,7 @@ (:require [clojure.core.protocols :as clojure.protocols] [methodical.impl.combo.common :as combo.common] - [methodical.interface] - [methodical.util.describe :as describe] + [methodical.interface :as i] [pretty.core :as pretty]) (:import (methodical.interface MethodCombination))) @@ -87,6 +86,6 @@ (datafy [this] {:class (class this)}) - describe/Describable + i/Describable (describe [this] (format "It uses the method combination [[%s]]." (.getCanonicalName (class this))))) diff --git a/src/methodical/impl/combo/operator.clj b/src/methodical/impl/combo/operator.clj index 6b5afa4..b0c79b5 100644 --- a/src/methodical/impl/combo/operator.clj +++ b/src/methodical/impl/combo/operator.clj @@ -45,8 +45,7 @@ [clojure.core.protocols :as clojure.protocols] [clojure.spec.alpha :as s] [methodical.impl.combo.common :as combo.common] - [methodical.interface] - [methodical.util.describe :as describe] + [methodical.interface :as i] [pretty.core :as pretty]) (:import (methodical.interface MethodCombination))) @@ -202,7 +201,7 @@ {:class (class this) :operator operator-name}) - describe/Describable + i/Describable (describe [this] (format "It uses the method combination [[%s]]\nwith the operator `%s`." (.getCanonicalName (class this)) diff --git a/src/methodical/impl/combo/threaded.clj b/src/methodical/impl/combo/threaded.clj index f92a298..9b2c062 100644 --- a/src/methodical/impl/combo/threaded.clj +++ b/src/methodical/impl/combo/threaded.clj @@ -3,8 +3,7 @@ (:require [clojure.core.protocols :as clojure.protocols] [methodical.impl.combo.common :as combo.common] - [methodical.interface] - [methodical.util.describe :as describe] + [methodical.interface :as i] [pretty.core :as pretty]) (:import (methodical.interface MethodCombination))) @@ -105,7 +104,7 @@ {:class (class this) :threading-type threading-type}) - describe/Describable + i/Describable (describe [this] (format "It uses the method combination [[%s]]\nwith the threading strategy `%s`." (.getCanonicalName (class this)) diff --git a/src/methodical/impl/dispatcher/everything.clj b/src/methodical/impl/dispatcher/everything.clj index ca568ad..8adbfb0 100644 --- a/src/methodical/impl/dispatcher/everything.clj +++ b/src/methodical/impl/dispatcher/everything.clj @@ -4,7 +4,6 @@ [clojure.core.protocols :as clojure.protocols] [methodical.impl.dispatcher.common :as dispatcher.common] [methodical.interface :as i] - [methodical.util.describe :as describe] [pretty.core :as pretty]) (:import (methodical.interface Dispatcher))) @@ -71,7 +70,7 @@ :hierarchy hierarchy-var :prefs prefs}) - describe/Describable + i/Describable (describe [this] (format "It uses the dispatcher [[%s]]\nwith hierarchy `%s`\nand prefs `%s`." (.getCanonicalName (class this)) diff --git a/src/methodical/impl/dispatcher/multi_default.clj b/src/methodical/impl/dispatcher/multi_default.clj index ba814e2..2c2f28e 100644 --- a/src/methodical/impl/dispatcher/multi_default.clj +++ b/src/methodical/impl/dispatcher/multi_default.clj @@ -7,7 +7,6 @@ [methodical.impl.dispatcher.common :as dispatcher.common] [methodical.impl.dispatcher.standard :as dispatcher.standard] [methodical.interface :as i] - [methodical.util.describe :as describe] [pretty.core :as pretty]) (:import (methodical.interface Dispatcher))) @@ -198,7 +197,7 @@ :hierarchy hierarchy-var :prefs prefs}) - describe/Describable + i/Describable (describe [this] (format "It uses the dispatcher [[%s]]\nwith hierarchy `%s`\nand prefs `%s`.\n\nThe default value is `%s`." (.getCanonicalName (class this)) diff --git a/src/methodical/impl/dispatcher/standard.clj b/src/methodical/impl/dispatcher/standard.clj index 46f5767..d0ad790 100644 --- a/src/methodical/impl/dispatcher/standard.clj +++ b/src/methodical/impl/dispatcher/standard.clj @@ -6,7 +6,6 @@ [clojure.core.protocols :as clojure.protocols] [methodical.impl.dispatcher.common :as dispatcher.common] [methodical.interface :as i] - [methodical.util.describe :as describe] [pretty.core :as pretty]) (:import (methodical.interface Dispatcher))) @@ -174,7 +173,7 @@ :hierarchy hierarchy-var :prefs prefs}) - describe/Describable + i/Describable (describe [this] (format "It uses the dispatcher [[%s]]\nwith hierarchy `%s`\nand prefs `%s`.\n\nThe default value is `%s`." (.getCanonicalName (class this)) diff --git a/src/methodical/impl/method_table/clojure.clj b/src/methodical/impl/method_table/clojure.clj index 10ef231..c89ea47 100644 --- a/src/methodical/impl/method_table/clojure.clj +++ b/src/methodical/impl/method_table/clojure.clj @@ -1,61 +1,89 @@ (ns methodical.impl.method-table.clojure (:require - [clojure.core.protocols :as clojure.protocols] - [methodical.impl.method-table.common :as method-table.common] - [methodical.interface] - [methodical.util.describe :as describe] - [pretty.core :as pretty]) + [clojure.core.protocols :as clojure.protocols] + [methodical.impl.method-table.common :as method-table.common] + [methodical.interface :as i] + [pretty.core :as pretty]) (:import - (methodical.interface MethodTable))) + (methodical.interface + MethodTable))) + (set! *warn-on-reflection* true) (comment methodical.interface/keep-me) -(deftype ClojureMethodTable [m] + +(deftype ClojureMethodTable + [m] + pretty/PrettyPrintable - (pretty [_] + + (pretty + [_] (if (seq m) (list 'clojure-method-table (count m) 'primary) '(clojure-method-table))) + Object - (equals [_ another] + + (equals + [_ another] (and (instance? ClojureMethodTable another) (= m (.m ^ClojureMethodTable another)))) + MethodTable - (primary-methods [_] + + (primary-methods + [_] m) - (aux-methods [_] + + (aux-methods + [_] nil) - (add-primary-method [this dispatch-val method] + + (add-primary-method + [this dispatch-val method] (let [new-m (assoc m dispatch-val method)] (if (= m new-m) this (ClojureMethodTable. new-m)))) - (remove-primary-method [this dispatch-val] + + (remove-primary-method + [this dispatch-val] (let [new-m (dissoc m dispatch-val)] (if (= m new-m) this (ClojureMethodTable. new-m)))) - (add-aux-method [_ _ _ _] + + (add-aux-method + [_ _ _ _] (throw (UnsupportedOperationException. "Clojure-style multimethods do not support auxiliary methods."))) - (remove-aux-method [_ _ _ _] + + (remove-aux-method + [_ _ _ _] (throw (UnsupportedOperationException. "Clojure-style multimethods do not support auxiliary methods."))) + clojure.protocols/Datafiable - (datafy [this] + + (datafy + [this] {:class (class this) :primary (method-table.common/datafy-primary-methods m)}) - describe/Describable - (describe [this] + + i/Describable + + (describe + [this] (format "It uses the method table [[%s]]. These primary methods are known:\n\n%s" (.getCanonicalName (class this)) (method-table.common/describe-primary-methods m)))) diff --git a/src/methodical/impl/method_table/common.clj b/src/methodical/impl/method_table/common.clj index cd5f37b..f09a72b 100644 --- a/src/methodical/impl/method_table/common.clj +++ b/src/methodical/impl/method_table/common.clj @@ -60,7 +60,7 @@ (str/split-lines (describe-method f)))))) (defn describe-primary-methods - "Helper for [[methodical.util.describe/describe]]ing the primary methods in a method table." + "Helper for [[methodical.interface/describe]]ing the primary methods in a method table." ^String [dispatch-value->method] (when (seq dispatch-value->method) (format @@ -71,7 +71,7 @@ (describe-method dispatch-value f)))))) (defn describe-aux-methods - "Helper for [[methodical.util.describe/describe]]ing the aux methods in a method table." + "Helper for [[methodical.interface/describe]]ing the aux methods in a method table." ^String [qualifier->dispatch-value->methods] (when (seq qualifier->dispatch-value->methods) (format diff --git a/src/methodical/impl/method_table/standard.clj b/src/methodical/impl/method_table/standard.clj index 7ce0b0b..0204d56 100644 --- a/src/methodical/impl/method_table/standard.clj +++ b/src/methodical/impl/method_table/standard.clj @@ -2,8 +2,7 @@ (:require [clojure.core.protocols :as clojure.protocols] [methodical.impl.method-table.common :as method-table.common] - [methodical.interface] - [methodical.util.describe :as describe] + [methodical.interface :as i] [pretty.core :as pretty]) (:import (methodical.interface MethodTable))) @@ -95,7 +94,7 @@ :primary (method-table.common/datafy-primary-methods primary) :aux (method-table.common/datafy-aux-methods aux)}) - describe/Describable + i/Describable (describe [this] (str (format "It uses the method table [[%s]]." (.getCanonicalName (class this))) (method-table.common/describe-primary-methods primary) diff --git a/src/methodical/impl/multifn/cached.clj b/src/methodical/impl/multifn/cached.clj index f473eda..b5c9593 100644 --- a/src/methodical/impl/multifn/cached.clj +++ b/src/methodical/impl/multifn/cached.clj @@ -3,7 +3,6 @@ [clojure.core.protocols :as clojure.protocols] [clojure.datafy :as datafy] [methodical.interface :as i] - [methodical.util.describe :as describe] [pretty.core :as pretty]) (:import (clojure.lang Named) @@ -81,8 +80,8 @@ :class (class this) :cache (datafy/datafy cache))) - describe/Describable + i/Describable (describe [_this] - (str (describe/describe cache) + (str (i/describe cache) \newline \newline - (describe/describe impl)))) + (i/describe impl)))) diff --git a/src/methodical/impl/multifn/standard.clj b/src/methodical/impl/multifn/standard.clj index f40104e..2934ff2 100644 --- a/src/methodical/impl/multifn/standard.clj +++ b/src/methodical/impl/multifn/standard.clj @@ -1,17 +1,22 @@ (ns methodical.impl.multifn.standard "Standard Methodical MultiFn impl." (:require - [clojure.core.protocols :as clojure.protocols] - [clojure.datafy :as datafy] - [methodical.impl.dispatcher.common :as dispatcher.common] - [methodical.interface :as i] - [methodical.util.describe :as describe] - [pretty.core :as pretty]) + [clojure.core.protocols :as clojure.protocols] + [clojure.datafy :as datafy] + [methodical.impl.dispatcher.common :as dispatcher.common] + [methodical.interface :as i] + [pretty.core :as pretty]) (:import - (methodical.interface Dispatcher MethodCombination MethodTable MultiFnImpl))) + (methodical.interface + Dispatcher + MethodCombination + MethodTable + MultiFnImpl))) + (set! *warn-on-reflection* true) + ;; "composite dispatch value" below just means a dispatch value consisting of multiple parts e.g. `[:x :y]` as opposed ;; to a single value like `:x`. @@ -19,9 +24,10 @@ "Sort dispatch values in order from most-specific-overall to least-specific-overall." [dispatcher dispatch-values] (sort-by - identity - (dispatcher.common/domination-comparator (partial i/dominates? dispatcher)) - dispatch-values)) + identity + (dispatcher.common/domination-comparator (partial i/dominates? dispatcher)) + dispatch-values)) + (defn non-composite-effective-dispatch-value "Operates only on non-composite dispatch values. Determine the effective (most-specific) dispatch value that will be @@ -54,6 +60,7 @@ most-specific-dispatch-value actual-dispatch-value))) + (defn composite-effective-dispatch-value "Combine multiple composite dispatch values into a single composite dispatch value that has the overall most-specific arg for each position, e.g. @@ -81,20 +88,22 @@ (filter sequential? method-dispatch-values)))) (range (count actual-dispatch-value)))))) + (defn effective-dispatch-value "Given matching `primary-methods` and `aux-methods` for the `actual-dispatch-value`, determine the effective dispatch value." [dispatcher actual-dispatch-value primary-methods aux-methods] (let [dispatch-values (transduce - (comp cat - (map meta) - (filter #(contains? % :dispatch-value)) - (map :dispatch-value)) - conj - [] - (cons primary-methods (vals aux-methods)))] + (comp cat + (map meta) + (filter #(contains? % :dispatch-value)) + (map :dispatch-value)) + conj + [] + (cons primary-methods (vals aux-methods)))] (composite-effective-dispatch-value dispatcher actual-dispatch-value dispatch-values))) + (defn standard-effective-method "Build an effective method using the 'standard' technique, taking the dispatch-value-method pairs in the `method-table`, finiding applicable ones using `dispatcher`, and combining them using `method-combination`." @@ -104,55 +113,82 @@ (some-> (i/combine-methods method-combination primary-methods aux-methods) (with-meta {:dispatch-value (effective-dispatch-value dispatcher dispatch-value primary-methods aux-methods)})))) -(deftype StandardMultiFnImpl [^MethodCombination combo - ^Dispatcher dispatcher - ^MethodTable method-table] + +(deftype StandardMultiFnImpl + [^MethodCombination combo + ^Dispatcher dispatcher + ^MethodTable method-table] + pretty/PrettyPrintable - (pretty [_this] + + (pretty + [_this] (list 'standard-multifn-impl combo dispatcher method-table)) + Object - (equals [_ another] + + (equals + [_ another] (and (instance? StandardMultiFnImpl another) (let [^StandardMultiFnImpl another another] (and (= combo (.combo another)) (= dispatcher (.dispatcher another)) (= method-table (.method-table another)))))) + MultiFnImpl - (method-combination [_this] + + (method-combination + [_this] combo) - (dispatcher [_this] + + (dispatcher + [_this] dispatcher) - (with-dispatcher [this new-dispatcher] + + (with-dispatcher + [this new-dispatcher] (if (= dispatcher new-dispatcher) this (StandardMultiFnImpl. combo new-dispatcher method-table))) - (method-table [_this] + + (method-table + [_this] method-table) - (with-method-table [this new-method-table] + + (with-method-table + [this new-method-table] (if (= method-table new-method-table) this (StandardMultiFnImpl. combo dispatcher new-method-table))) - (effective-method [_this dispatch-value] + + (effective-method + [_this dispatch-value] (standard-effective-method combo dispatcher method-table dispatch-value)) + clojure.protocols/Datafiable - (datafy [this] + + (datafy + [this] {:class (class this) :combo (datafy/datafy combo) :dispatcher (datafy/datafy dispatcher) :method-table (datafy/datafy method-table)}) - describe/Describable - (describe [_this] - (str (describe/describe combo) + + i/Describable + + (describe + [_this] + (str (i/describe combo) \newline \newline - (describe/describe dispatcher) + (i/describe dispatcher) \newline \newline - (describe/describe method-table)))) + (i/describe method-table)))) diff --git a/src/methodical/impl/standard.clj b/src/methodical/impl/standard.clj index 924efd8..5b54d9b 100644 --- a/src/methodical/impl/standard.clj +++ b/src/methodical/impl/standard.clj @@ -3,7 +3,6 @@ [clojure.core.protocols :as clojure.protocols] [clojure.datafy :as datafy] [methodical.interface :as i] - [methodical.util.describe :as describe] [pretty.core :as pretty]) (:import (clojure.lang Named) @@ -243,7 +242,7 @@ {:class (class this)}) mta)) - describe/Describable + i/Describable (describe [_this] (let [{mf-name :name, mf-ns :ns, :keys [file line]} mta] (str (pr-str mf-name) @@ -257,7 +256,7 @@ (when (seq message) (format " is defined in %s." message))) \newline \newline - (describe/describe impl))))) + (i/describe impl))))) (defn multifn? "True if `x` is an instance of `StandardMultiFn`." diff --git a/src/methodical/interface.clj b/src/methodical/interface.clj index 54933ec..15617f8 100644 --- a/src/methodical/interface.clj +++ b/src/methodical/interface.clj @@ -1,19 +1,27 @@ (ns methodical.interface (:refer-clojure :exclude [isa? prefers]) - (:require clojure.core)) + (:require + [clojure.core] + [clojure.datafy :as datafy] + [potemkin.types :as p.types])) + (set! *warn-on-reflection* true) + ;; this is a dummy dependency until Cloverage 1.3.0 is released -- see ;; https://github.com/cloverage/cloverage/issues/318 (comment clojure.core/keep-me) + (defprotocol MethodCombination "A *method combination* defines the way applicable primary and auxiliary methods are combined into a single *effective method*. Method combinations also specify which auxiliary method *qualifiers* (e.g. `:before` or `:around`) are allowed, and how `defmethod` macro forms using those qualifiers are expanded (e.g., whether they get an implicit `next-method` arg)." - (allowed-qualifiers [method-combination] + + (allowed-qualifiers + [method-combination] "The set containing all qualifiers supported by this method combination. `nil` in the set means the method combination supports primary methods (because primary methods have no qualifier); all other values refer to auxiliary methods with that qualifier, e.g. `:before`, `:after`, or `:around`. @@ -24,23 +32,29 @@ (allowed-qualifiers (doseq-method-combination)) ;-> #{:doseq} ```") - (combine-methods [method-combination primary-methods aux-methods] + (combine-methods + [method-combination primary-methods aux-methods] "Combine a sequence of matching `primary-methods` with `aux-methods` (a map of qualifier -> sequence of methods) into a single effective method. Method includes effective `^:dispatch-value` metadata.") - (transform-fn-tail [method-combination qualifier fn-tail] + (transform-fn-tail + [method-combination qualifier fn-tail] "Make appropriate transformations to the `fn-tail` of a [[methodical.macros/defmethod]] macro expansion for a primary method (qualifier will be `nil`) or an auxiliary method. You can use this method to add implicit args like `next-method` to the body of a `defmethod` macro. (Because this method is invoked during macroexpansion, it should return a Clojure form.)")) + (defprotocol MethodTable "A *method table* stores primary and auxiliary methods, and returns them when asked. The default implementation, [[methodical.impl/standard-method-table]], uses simple Clojure immutable maps." - (primary-methods [method-table] + + (primary-methods + [method-table] "Get a `dispatch-value -> fn` map of all primary methods associated with this method table.") - (aux-methods [method-table] + (aux-methods + [method-table] "Get a `qualifier -> dispatch-value -> [fn]` map of all auxiliary methods associated with this method table.") (add-primary-method ^methodical.interface.MethodTable [method-table dispatch-value f] @@ -61,11 +75,13 @@ In the future, I hope to fix this by storing unique indentifiers in the metadata of methods in the map.")) + (defprotocol Dispatcher "A *dispatcher* decides which dispatch value should be used for a given set of arguments, which primary and auxiliary methods from the *method table* are applicable for that dispatch value, and the order those methods should be applied in -- which methods are most specific, and which are the least specific (e.g., `String` is more-specific than `Object`)." + (dispatch-value [dispatcher] [dispatcher a] @@ -76,29 +92,36 @@ "Return an appropriate dispatch value for args passed to a multimethod. (This method is equivalent in purpose to the dispatch function of vanilla Clojure multimethods.)") - (matching-primary-methods [dispatcher method-table dispatch-value] + (matching-primary-methods + [dispatcher method-table dispatch-value] "Return a sequence of applicable primary methods for `dispatch-value`, sorted from most-specific to least-specific. Methods should have the `^:dispatch-value` with which they were defined as metadata. The standard dispatcher also checks to make sure methods in the sequence are not ambiguously specific, replacing ambiguous methods with ones that will throw an Exception when invoked.") - (matching-aux-methods [dispatcher method-table dispatch-value] + (matching-aux-methods + [dispatcher method-table dispatch-value] "Return a map of aux method qualifier -> sequence of applicable methods for `dispatch-value`, sorted from most-specific to least-specific. Methods should have the `^:dispatch-value` with which they were defined as metadata.") - (default-dispatch-value [dispatcher] + (default-dispatch-value + [dispatcher] "Default dispatch value to use if no other dispatch value matches.") - (prefers [dispatcher] + (prefers + [dispatcher] "Return a map of preferred dispatch value -> set of other dispatch values.") - (^methodical.interface.Dispatcher ^{:style/indent :defn} with-prefers [dispatcher new-prefs] + (^methodical.interface.Dispatcher ^{:style/indent :defn} with-prefers + [dispatcher new-prefs] "Return a copy of `dispatcher` with its preferences map replaced with `new-prefs`.") - (dominates? [dispatcher dispatch-val-x dispatch-val-y] + (dominates? + [dispatcher dispatch-val-x dispatch-val-y] "Is `dispatch-val-x` considered more specific than `dispatch-val-y`?")) + (defprotocol MultiFnImpl "Protocol for a complete Methodical multimethod, excluding the optional cache (multimethods with caching wrap a `MultiFnImpl`). Methodical multimethods are divided into four components: a *method combination*, which @@ -107,39 +130,67 @@ implements [[methodical.interface/Dispatcher]]; and, optionally, a *cache*, which implements [[methodical.interface/Cache]]. The methods in *this* protocol are used to access or modify the various constituent parts of a methodical multimethod, and to use them in concert to create an *effective method*." - (^methodical.interface.MethodCombination method-combination [multifn] - "Get the method combination associated with this multifn.") - (^methodical.interface.Dispatcher dispatcher [multifn] - "Get the dispatcher associated with this multifn.") + (^methodical.interface.MethodCombination method-combination + [multifn] + "Get the method combination associated with this multifn.") + + (^methodical.interface.Dispatcher dispatcher + [multifn] + "Get the dispatcher associated with this multifn.") - (^methodical.interface.MultiFnImpl ^{:style/indent :defn} with-dispatcher [multifn new-dispatcher] - "Return a copy of this multifn using `new-dispatcher` as its dispatcher.") + (^methodical.interface.MultiFnImpl ^{:style/indent :defn} with-dispatcher + [multifn new-dispatcher] + "Return a copy of this multifn using `new-dispatcher` as its dispatcher.") - (^methodical.interface.MethodTable method-table [multifn] - "Get the method table associated with this multifn.") + (^methodical.interface.MethodTable method-table + [multifn] + "Get the method table associated with this multifn.") - (^methodical.interface.MultiFnImpl ^{:style/indent :defn} with-method-table [multifn new-method-table] - "Return a copy of this multifn using `new-method-table` as its method table.") + (^methodical.interface.MultiFnImpl ^{:style/indent :defn} with-method-table + [multifn new-method-table] + "Return a copy of this multifn using `new-method-table` as its method table.") - (effective-method [multifn dispatch-value] + (effective-method + [multifn dispatch-value] "Return the effective method for `dispatch-value`. The effective method is a combined primary method and applicable auxiliary methods that can be called like a normal function. [[effective-method]] is similar in purpose to [[clojure.core/get-method]] in vanilla Clojure multimethods; a different name is used here because I felt `get-method` would be ambiguous with regards to whether it returns only a primary method or a combined effective method.")) + (defprotocol Cache "A *cache*, if present, implements a caching strategy for effective methods, so that they need not be recomputed on every invocation." - (cached-method [cache dispatch-value] + + (cached-method + [cache dispatch-value] "Return cached effective method for `dispatch-value`, if it exists in the cache.") - (cache-method! [cache dispatch-value method] + (cache-method! + [cache dispatch-value method] "Cache the effective method for `dispatch-value` in this cache.") - (clear-cache! [cache] + (clear-cache! + [cache] "Empty the contents of the cache in-place.") - (^methodical.interface.Cache empty-copy [cache] - "Return an empty copy of the same type as this cache, e.g. for use when copying a multifn.")) + (^methodical.interface.Cache empty-copy + [cache] + "Return an empty copy of the same type as this cache, e.g. for use when copying a multifn.")) + + + +(p.types/defprotocol+ Describable + (describe ^String [this] + "Return a Markdown-formatted string description of a Methodical object, such as a multifn.")) + +(extend-protocol Describable + nil + (describe [_this] + "nil") + + Object + (describe [this] + (pr-str (datafy/datafy this)))) diff --git a/src/methodical/util.clj b/src/methodical/util.clj index 1774238..024c144 100644 --- a/src/methodical/util.clj +++ b/src/methodical/util.clj @@ -4,8 +4,7 @@ (:refer-clojure :exclude [prefers prefer-method remove-all-methods]) (:require [methodical.impl.standard :as impl.standard] - [methodical.interface :as i] - [methodical.util.describe :as describe])) + [methodical.interface :as i])) (set! *warn-on-reflection* true) @@ -284,7 +283,7 @@ ;;;; #### Low-level destructive operations (defn ^:no-doc docstring-with-describe-output-appended - "Build a docstring by taking the original user-supplied `:doc` and the output of [[describe/describe]]." + "Build a docstring by taking the original user-supplied `:doc` and the output of [[i/describe]]." (^String [varr] (let [original-doc ((some-fn :original-doc :doc) (meta varr)) updated-value (var-get varr)] @@ -294,7 +293,7 @@ (str (when (seq original-doc) (str original-doc \newline \newline)) - (describe/describe updated-value)))) + (i/describe updated-value)))) (defn alter-var-root+ "Like [[clojure.core/alter-var-root]], but handles vars that are aliases of other vars, e.g. ones that have been diff --git a/src/methodical/util/describe.clj b/src/methodical/util/describe.clj deleted file mode 100644 index 2ce29e5..0000000 --- a/src/methodical/util/describe.clj +++ /dev/null @@ -1,16 +0,0 @@ -(ns methodical.util.describe - (:require [clojure.datafy :as datafy] - [potemkin.types :as p.types])) - -(p.types/defprotocol+ Describable - (describe ^String [this] - "Return a Markdown-formatted string description of a Methodical object, such as a multifn.")) - -(extend-protocol Describable - nil - (describe [_this] - "nil") - - Object - (describe [this] - (pr-str (datafy/datafy this)))) diff --git a/test/methodical/util/describe_test.clj b/test/methodical/util/describe_test.clj index 5bc2fd2..437f906 100644 --- a/test/methodical/util/describe_test.clj +++ b/test/methodical/util/describe_test.clj @@ -3,7 +3,7 @@ [clojure.string :as str] [clojure.test :as t] [methodical.core :as m] - [methodical.util.describe :as describe])) + [methodical.interface :as i])) (defonce ^:private dispatch-first (fn [x _y] @@ -70,7 +70,7 @@ (t/deftest describe-test (t/is (= expected-description - (str/split-lines (describe/describe mf))))) + (str/split-lines (i/describe mf))))) (t/deftest update-docstrings-test (t/is (= (concat