diff --git a/.gitignore b/.gitignore index 8997057..19b0d19 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ /tags /target \#*\# +/.cljs_node_repl diff --git a/project.clj b/project.clj index 4c10400..1cfc72f 100644 --- a/project.clj +++ b/project.clj @@ -27,7 +27,8 @@ :dependencies [[pretty "1.0.0"] - [potemkin "0.4.5"]] + [potemkin "0.4.5"] + [org.clojure/clojurescript "1.10.520" :scope "provided"]] :aot [methodical.interface methodical.impl.standard] diff --git a/src/methodical/impl/cache/simple.clj b/src/methodical/impl/cache/simple.clj index 4a35a19..a10b961 100644 --- a/src/methodical/impl/cache/simple.clj +++ b/src/methodical/impl/cache/simple.clj @@ -2,25 +2,24 @@ "A basic, dumb cache. `SimpleCache` stores cached methods in a simple map of dispatch-value -> effective method; it offers no facilities to deduplicate identical methods for the same dispatch value. This behaves similarly to the caching mechanism in vanilla Clojure." - (:require [potemkin.types :as p.types] - [pretty.core :refer [PrettyPrintable]]) + (:require [pretty.core :refer [PrettyPrintable]]) (:import methodical.interface.Cache)) -(p.types/deftype+ SimpleCache [atomm] +(deftype SimpleCache [atomm] PrettyPrintable (pretty [_] '(simple-cache)) Cache - (cached-method [_ dispatch-value] + (cachedMethod [_ dispatch-value] (get @atomm dispatch-value)) - (cache-method! [_ dispatch-value method] + (cacheMethodBang [_ dispatch-value method] (swap! atomm assoc dispatch-value method)) - (clear-cache! [this] + (clearCacheBang [this] (reset! atomm {}) this) - (empty-copy [this] + (emptyCopy [this] (SimpleCache. (atom {})))) diff --git a/src/methodical/impl/cache/watching.clj b/src/methodical/impl/cache/watching.clj index 7b7995f..3634b93 100644 --- a/src/methodical/impl/cache/watching.clj +++ b/src/methodical/impl/cache/watching.clj @@ -12,14 +12,13 @@ finalized (which, of course, may actually be never -- but worst-case is that some unneeded calls to `clear-cache!` get made)." (:require [methodical.interface :as i] - [potemkin.types :as p.types] [pretty.core :refer [PrettyPrintable]]) (:import java.lang.ref.WeakReference methodical.interface.Cache)) (declare add-watches remove-watches) -(p.types/deftype+ WatchingCache [^Cache cache watch-key refs] +(deftype WatchingCache [^Cache cache watch-key refs] PrettyPrintable (pretty [_] (concat ['watching-cache cache 'watching] refs)) @@ -29,18 +28,18 @@ (remove-watches this)) Cache - (cached-method [_ dispatch-value] - (.cached-method cache dispatch-value)) + (cachedMethod [_ dispatch-value] + (.cachedMethod cache dispatch-value)) - (cache-method! [this dispatch-value method] - (.cache-method! cache dispatch-value method) + (cacheMethodBang [this dispatch-value method] + (.cacheMethodBang cache dispatch-value method) this) - (clear-cache! [this] - (.clear-cache! cache) + (clearCacheBang [this] + (.clearCacheBang cache) this) - (empty-copy [this] + (emptyCopy [this] (add-watches (i/empty-copy cache) refs))) (defn- cache-watch-fn [cache] diff --git a/src/methodical/impl/combo/clojure.clj b/src/methodical/impl/combo/clojure.clj index 813c103..d528d36 100644 --- a/src/methodical/impl/combo/clojure.clj +++ b/src/methodical/impl/combo/clojure.clj @@ -1,11 +1,10 @@ (ns methodical.impl.combo.clojure "Simple method combination strategy that mimics the way vanilla Clojure multimethods combine methods; that is, to say, not at all. Like vanilla Clojure multimethods, this method combination only supports primary methods." - (:require [potemkin.types :as p.types] - [pretty.core :refer [PrettyPrintable]]) + (:require [pretty.core :refer [PrettyPrintable]]) (:import methodical.interface.MethodCombination)) -(p.types/deftype+ ClojureMethodCombination [] +(deftype ClojureMethodCombination [] PrettyPrintable (pretty [_] '(clojure-method-combination)) @@ -15,13 +14,13 @@ (instance? ClojureMethodCombination another)) MethodCombination - (allowed-qualifiers [_] + (allowedQualifiers [_] #{nil}) ; only primary methods - (combine-methods [_ [primary-method] aux-methods] + (combineMethods [_ [primary-method] aux-methods] (when (seq aux-methods) (throw (UnsupportedOperationException. "Clojure-style multimethods do not support auxiliary methods."))) primary-method) - (transform-fn-tail [_ _ fn-tail] + (transformFnTail [_ _ fn-tail] fn-tail)) diff --git a/src/methodical/impl/combo/clos.clj b/src/methodical/impl/combo/clos.clj index b4b289e..b9be6aa 100644 --- a/src/methodical/impl/combo/clos.clj +++ b/src/methodical/impl/combo/clos.clj @@ -4,7 +4,6 @@ are ignored. Primary methods and around methods get an implicit `next-method` arg (see Methodical dox for more on what this means)." (:require [methodical.impl.combo.common :as combo.common] - [potemkin.types :as p.types] [pretty.core :refer [PrettyPrintable]]) (:import methodical.interface.MethodCombination)) @@ -53,7 +52,7 @@ result)] (comp apply-afters combined-method)))) -(p.types/deftype+ CLOSStandardMethodCombination [] +(deftype CLOSStandardMethodCombination [] PrettyPrintable (pretty [_] '(clos-method-combination)) @@ -63,14 +62,14 @@ (instance? CLOSStandardMethodCombination another)) MethodCombination - (allowed-qualifiers [_] + (allowedQualifiers [_] #{nil :before :after :around}) - (combine-methods [_ primary-methods {:keys [before after around]}] + (combineMethods [_ primary-methods {:keys [before after around]}] (some-> (combo.common/combine-primary-methods primary-methods) (apply-befores before) (apply-afters after) (combo.common/apply-around-methods around))) - (transform-fn-tail [_ qualifier fn-tail] + (transformFnTail [_ qualifier fn-tail] (combo.common/add-implicit-next-method-args qualifier fn-tail))) diff --git a/src/methodical/impl/combo/operator.clj b/src/methodical/impl/combo/operator.clj index ce5e161..7d37612 100644 --- a/src/methodical/impl/combo/operator.clj +++ b/src/methodical/impl/combo/operator.clj @@ -38,7 +38,6 @@ ...)" (:refer-clojure :exclude [methods]) (:require [methodical.impl.combo.common :as combo.common] - [potemkin.types :as p.types] [pretty.core :refer [PrettyPrintable]]) (:import methodical.interface.MethodCombination)) @@ -152,7 +151,7 @@ ;;;; ### `OperatorMethodCombination` -(p.types/deftype+ OperatorMethodCombination [operator-name] +(deftype OperatorMethodCombination [operator-name] PrettyPrintable (pretty [_] (list 'operator-method-combination operator-name)) @@ -163,15 +162,15 @@ (= operator-name (.operator-name ^OperatorMethodCombination another)))) MethodCombination - (allowed-qualifiers [_] + (allowedQualifiers [_] #{nil :around}) - (combine-methods [_ primary-methods {:keys [around]}] + (combineMethods [_ primary-methods {:keys [around]}] (when (seq primary-methods) (-> ((operator operator-name) primary-methods) (combo.common/apply-around-methods around)))) - (transform-fn-tail [_ qualifier fn-tail] + (transformFnTail [_ qualifier fn-tail] (if (= qualifier :around) (combo.common/add-implicit-next-method-args qualifier fn-tail) fn-tail))) diff --git a/src/methodical/impl/combo/threaded.clj b/src/methodical/impl/combo/threaded.clj index 688d916..3a2e227 100644 --- a/src/methodical/impl/combo/threaded.clj +++ b/src/methodical/impl/combo/threaded.clj @@ -1,7 +1,6 @@ (ns methodical.impl.combo.threaded (:refer-clojure :exclude [methods]) (:require [methodical.impl.combo.common :as combo.common] - [potemkin.types :as p.types] [pretty.core :refer [PrettyPrintable]]) (:import methodical.interface.MethodCombination)) @@ -70,25 +69,24 @@ (apply method (conj butlast* last*)))])))) -(p.types/deftype+ ThreadingMethodCombination [threading-type] +(deftype ThreadingMethodCombination [threading-type] PrettyPrintable (pretty [_] (list 'threading-method-combination threading-type)) - MethodCombination Object (equals [_ another] (and (instance? ThreadingMethodCombination another) (= threading-type (.threading-type ^ThreadingMethodCombination another)))) MethodCombination - (allowed-qualifiers [_] + (allowedQualifiers [_] #{nil :before :after :around}) - (combine-methods [_ primary-methods aux-methods] + (combineMethods [_ primary-methods aux-methods] (combine-with-threader (threading-invoker threading-type) primary-methods aux-methods)) - (transform-fn-tail [_ qualifier fn-tail] + (transformFnTail [_ qualifier fn-tail] (combo.common/add-implicit-next-method-args qualifier fn-tail))) (defn threading-method-combination diff --git a/src/methodical/impl/dispatcher/common.clj b/src/methodical/impl/dispatcher/common.cljc similarity index 93% rename from src/methodical/impl/dispatcher/common.clj rename to src/methodical/impl/dispatcher/common.cljc index 08a1938..c2871c3 100644 --- a/src/methodical/impl/dispatcher/common.clj +++ b/src/methodical/impl/dispatcher/common.cljc @@ -1,5 +1,10 @@ (ns methodical.impl.dispatcher.common - "Utility functions for implementing Dispatchers.") + "Utility functions for implementing Dispatchers." + #?(:cljs + (:require + [goog.string :refer [format]]))) + +#?(:cljs (def ^:private IllegalStateException js/Error)) (defn add-preference "Add a method preference to `prefs` for dispatch value `x` over `y`. Used to implement `prefer-method`." diff --git a/src/methodical/impl/dispatcher/everything.clj b/src/methodical/impl/dispatcher/everything.clj index 8c20cda..0f23a18 100644 --- a/src/methodical/impl/dispatcher/everything.clj +++ b/src/methodical/impl/dispatcher/everything.clj @@ -1,11 +1,10 @@ (ns methodical.impl.dispatcher.everything (:require [methodical.impl.dispatcher.common :as dispatcher.common] [methodical.interface :as i] - [potemkin.types :as p.types] [pretty.core :refer [PrettyPrintable]]) (:import methodical.interface.Dispatcher)) -(p.types/deftype+ EverythingDispatcher [hierarchy-var prefs] +(deftype EverythingDispatcher [hierarchy-var prefs] PrettyPrintable (pretty [_] (cons @@ -26,31 +25,31 @@ (= prefs (.prefs another)))))) Dispatcher - (dispatch-value [_] nil) - (dispatch-value [_ a] nil) - (dispatch-value [_ a b] nil) - (dispatch-value [_ a b c] nil) - (dispatch-value [_ a b c d] nil) - (dispatch-value [_ a b c d more] nil) - - (matching-primary-methods [_ method-table _] + (dispatchValue [_] nil) + (dispatchValue [_ a] nil) + (dispatchValue [_ a b] nil) + (dispatchValue [_ a b c] nil) + (dispatchValue [_ a b c d] nil) + (dispatchValue [_ a b c d more] nil) + + (matchingPrimaryMethods [_ method-table _] (let [primary-methods (i/primary-methods method-table) comparitor (dispatcher.common/domination-comparitor (var-get hierarchy-var) prefs ::no-dispatch-value)] (map second (sort-by first comparitor primary-methods)))) - (matching-aux-methods [_ method-table _] + (matchingAuxMethods [_ method-table _] (let [aux-methods (i/aux-methods method-table) comparitor (dispatcher.common/domination-comparitor (var-get hierarchy-var) prefs ::no-dispatch-value)] (into {} (for [[qualifier dispatch-value->methods] aux-methods] [qualifier (mapcat second (sort-by first comparitor dispatch-value->methods))])))) - (default-dispatch-value [_] + (defaultDispatchValue [_] nil) (prefers [_] prefs) - (prefer-method [this x y] + (preferMethod [this x y] (let [new-prefs (dispatcher.common/add-preference (partial isa? (var-get hierarchy-var)) prefs x y)] (if (= prefs new-prefs) this diff --git a/src/methodical/impl/dispatcher/standard.clj b/src/methodical/impl/dispatcher/standard.cljc similarity index 78% rename from src/methodical/impl/dispatcher/standard.clj rename to src/methodical/impl/dispatcher/standard.cljc index 68b6f39..7d46a2f 100644 --- a/src/methodical/impl/dispatcher/standard.clj +++ b/src/methodical/impl/dispatcher/standard.cljc @@ -4,9 +4,12 @@ (:refer-clojure :exclude [prefers prefer-method]) (:require [methodical.impl.dispatcher.common :as dispatcher.common] [methodical.interface :as i] - [potemkin.types :as p.types] - [pretty.core :refer [PrettyPrintable]]) - (:import methodical.interface.Dispatcher)) + #?(:clj [pretty.core :refer [PrettyPrintable]]) + #?(:cljs [methodical.interface :refer [Dispatcher]]) + #?(:cljs [goog.string :refer [format]])) + #?(:clj (:import methodical.interface.Dispatcher))) + +#?(:cljs (def ^:private IllegalArgumentException js/Error)) (defn- matching-primary-pairs-excluding-default "Return a sequence of pairs of `[dispatch-value method]` for all applicable dispatch values, excluding the default @@ -87,20 +90,23 @@ :when (seq pairs)] [qualifier (map second pairs)]))) +(deftype StandardDispatcher [dispatch-fn hierarchy-var default-value prefs] + #?@(:clj + [PrettyPrintable + (pretty [_] + (concat ['standard-dispatcher dispatch-fn] + (when (not= hierarchy-var #'clojure.core/global-hierarchy) + [:hierarchy hierarchy-var]) + (when (not= default-value :default) + [:default-value default-value]) + (when (seq prefs) + [:prefers prefs])))]) -(p.types/deftype+ StandardDispatcher [dispatch-fn hierarchy-var default-value prefs] - PrettyPrintable - (pretty [_] - (concat ['standard-dispatcher dispatch-fn] - (when (not= hierarchy-var #'clojure.core/global-hierarchy) - [:hierarchy hierarchy-var]) - (when (not= default-value :default) - [:default-value default-value]) - (when (seq prefs) - [:prefers prefs]))) + #?(:clj Object + :cljs IEquiv) + ;; todo: hashcode - Object - (equals [_ another] + (#?(:clj equals, :cljs -equiv) [_ another] (and (instance? StandardDispatcher another) (let [^StandardDispatcher another another] @@ -110,27 +116,29 @@ (= default-value (.default-value another)) (= prefs (.prefs another)))))) - Dispatcher - (dispatch-value [_] (dispatch-fn)) - (dispatch-value [_ a] (dispatch-fn a)) - (dispatch-value [_ a b] (dispatch-fn a b)) - (dispatch-value [_ a b c] (dispatch-fn a b c)) - (dispatch-value [_ a b c d] (dispatch-fn a b c d)) - (dispatch-value [_ a b c d more] (apply dispatch-fn a b c d more)) + #?(:clj Dispatcher :cljs Object) + (dispatchValue [_] (dispatch-fn)) + (dispatchValue [_ a] (dispatch-fn a)) + (dispatchValue [_ a b] (dispatch-fn a b)) + (dispatchValue [_ a b c] (dispatch-fn a b c)) + (dispatchValue [_ a b c d] (dispatch-fn a b c d)) + (dispatchValue [_ a b c d more] (apply dispatch-fn a b c d more)) - (matching-primary-methods [_ method-table dispatch-value] + (matchingPrimaryMethods [_ method-table dispatch-value] (matching-primary-methods (var-get hierarchy-var) prefs default-value method-table dispatch-value)) - (matching-aux-methods [_ method-table dispatch-value] + (matchingAuxMethods [_ method-table dispatch-value] (matching-aux-methods (var-get hierarchy-var) prefs default-value method-table dispatch-value)) - (default-dispatch-value [_] + (defaultDispatchValue [_] default-value) (prefers [_] prefs) - (prefer-method [this x y] + (preferMethod [this x y] + ;; var-get is not implemented in cljs + ;; https://github.com/camsaul/methodical/issues/29 (let [new-prefs (dispatcher.common/add-preference (partial isa? (var-get hierarchy-var)) prefs x y)] (if (= prefs new-prefs) this diff --git a/src/methodical/impl/method_table/clojure.clj b/src/methodical/impl/method_table/clojure.clj index c731d77..7ff0d4b 100644 --- a/src/methodical/impl/method_table/clojure.clj +++ b/src/methodical/impl/method_table/clojure.clj @@ -1,9 +1,8 @@ (ns methodical.impl.method-table.clojure - (:require [potemkin.types :as p.types] - [pretty.core :refer [PrettyPrintable]]) + (:require [pretty.core :refer [PrettyPrintable]]) (:import methodical.interface.MethodTable)) -(p.types/deftype+ ClojureMethodTable [m] +(deftype ClojureMethodTable [m] PrettyPrintable (pretty [_] (if (seq m) @@ -16,26 +15,26 @@ (= m (.m ^ClojureMethodTable another)))) MethodTable - (primary-methods [_] + (primaryMethods [_] m) - (aux-methods [_] + (auxMethods [_] nil) - (add-primary-method [this dispatch-val method] + (addPrimaryMethod [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] + (removePrimaryMethod [this dispatch-val] (let [new-m (dissoc m dispatch-val)] (if (= m new-m) this (ClojureMethodTable. new-m)))) - (add-aux-method [_ _ _ _] + (addAuxMethod [_ _ _ _] (throw (UnsupportedOperationException. "Clojure-style multimethods do not support auxiliary methods."))) - (remove-aux-method [_ _ _ _] + (removeAuxMethod [_ _ _ _] (throw (UnsupportedOperationException. "Clojure-style multimethods do not support auxiliary methods.")))) diff --git a/src/methodical/impl/method_table/standard.clj b/src/methodical/impl/method_table/standard.clj index 32b7b6d..43dedac 100644 --- a/src/methodical/impl/method_table/standard.clj +++ b/src/methodical/impl/method_table/standard.clj @@ -1,9 +1,8 @@ (ns methodical.impl.method-table.standard - (:require [potemkin.types :as p.types] - [pretty.core :refer [PrettyPrintable]]) + (:require [pretty.core :refer [PrettyPrintable]]) (:import methodical.interface.MethodTable)) -(p.types/deftype+ StandardMethodTable [primary aux] +(deftype StandardMethodTable [primary aux] PrettyPrintable (pretty [_] (cons @@ -24,25 +23,25 @@ (= aux (.aux ^StandardMethodTable another)))) MethodTable - (primary-methods [_] + (primaryMethods [_] primary) - (aux-methods [_] + (auxMethods [_] aux) - (add-primary-method [this dispatch-val method] + (addPrimaryMethod [this dispatch-val method] (let [new-primary (assoc primary dispatch-val method)] (if (= primary new-primary) this (StandardMethodTable. new-primary aux)))) - (remove-primary-method [this dispatch-val] + (removePrimaryMethod [this dispatch-val] (let [new-primary (dissoc primary dispatch-val)] (if (= primary new-primary) this (StandardMethodTable. new-primary aux)))) - (add-aux-method [this qualifier dispatch-value method] + (addAuxMethod [this qualifier dispatch-value method] (let [new-aux (update-in aux [qualifier dispatch-value] (fn [existing-methods] (if (contains? (set existing-methods) method) existing-methods @@ -51,7 +50,7 @@ this (StandardMethodTable. primary new-aux)))) - (remove-aux-method [this qualifier dispatch-value method] + (removeAuxMethod [this qualifier dispatch-value method] (let [xforms [(fn [aux] (update-in aux [qualifier dispatch-value] (fn [defined-methods] (remove #(= % method) defined-methods)))) diff --git a/src/methodical/impl/multifn/cached.clj b/src/methodical/impl/multifn/cached.clj index 100780b..ca2bfcc 100644 --- a/src/methodical/impl/multifn/cached.clj +++ b/src/methodical/impl/multifn/cached.clj @@ -1,10 +1,9 @@ (ns methodical.impl.multifn.cached (:require [methodical.interface :as i] - [potemkin.types :as p.types] [pretty.core :refer [PrettyPrintable]]) (:import [methodical.interface Cache MultiFnImpl])) -(p.types/deftype+ CachedMultiFnImpl [^MultiFnImpl impl, ^Cache cache] +(deftype CachedMultiFnImpl [^MultiFnImpl impl, ^Cache cache] PrettyPrintable (pretty [_] (list 'cached-multifn-impl impl cache)) @@ -17,30 +16,30 @@ (= (class cache) (class (.cache ^CachedMultiFnImpl another))))) MultiFnImpl - (method-combination [_] + (methodCombination [_] (i/method-combination impl)) (dispatcher [_] (.dispatcher impl)) - (with-dispatcher [this new-dispatcher] + (withDispatcher [this new-dispatcher] (let [new-impl (i/with-dispatcher impl new-dispatcher)] (if (= impl new-impl) this (CachedMultiFnImpl. new-impl (i/empty-copy cache))))) - (method-table [_] + (methodTable [_] (i/method-table impl)) - (with-method-table [this new-method-table] + (withMethodTable [this new-method-table] (let [new-impl (i/with-method-table impl new-method-table)] (if (= impl new-impl) this (CachedMultiFnImpl. new-impl (i/empty-copy cache))))) - (effective-method [_ dispatch-value] + (effectiveMethod [_ dispatch-value] (or - (.cached-method cache dispatch-value) + (.cachedMethod cache dispatch-value) (let [method (i/effective-method impl dispatch-value)] (i/cache-method! cache dispatch-value method) method)))) diff --git a/src/methodical/impl/multifn/standard.clj b/src/methodical/impl/multifn/standard.clj index 2ca8115..c6e7132 100644 --- a/src/methodical/impl/multifn/standard.clj +++ b/src/methodical/impl/multifn/standard.clj @@ -1,7 +1,6 @@ (ns methodical.impl.multifn.standard "Standard Methodical MultiFn impl, which " (:require [methodical.interface :as i] - [potemkin.types :as p.types] [pretty.core :refer [PrettyPrintable]]) (:import [methodical.interface Dispatcher MethodCombination MethodTable MultiFnImpl])) @@ -13,9 +12,9 @@ aux-methods (i/matching-aux-methods dispatcher method-table dispatch-value)] (i/combine-methods method-combination primary-methods aux-methods))) -(p.types/deftype+ StandardMultiFnImpl [^MethodCombination combo - ^Dispatcher dispatcher - ^MethodTable method-table] +(deftype StandardMultiFnImpl [^MethodCombination combo + ^Dispatcher dispatcher + ^MethodTable method-table] PrettyPrintable (pretty [_] (list 'standard-multifn-impl combo dispatcher method-table)) @@ -29,24 +28,24 @@ (= method-table (.method-table another)))))) MultiFnImpl - (method-combination [_] + (methodCombination [_] combo) (dispatcher [_] dispatcher) - (with-dispatcher [this new-dispatcher] + (withDispatcher [this new-dispatcher] (if (= dispatcher new-dispatcher) this (StandardMultiFnImpl. combo new-dispatcher method-table))) - (method-table [_] + (methodTable [_] method-table) - (with-method-table [this new-method-table] + (withMethodTable [this new-method-table] (if (= method-table new-method-table) this (StandardMultiFnImpl. combo dispatcher new-method-table))) - (effective-method [_ dispatch-value] + (effectiveMethod [_ dispatch-value] (standard-effective-method combo dispatcher method-table dispatch-value))) diff --git a/src/methodical/impl/standard.clj b/src/methodical/impl/standard.clj index cf60bfc..ff94a0a 100644 --- a/src/methodical/impl/standard.clj +++ b/src/methodical/impl/standard.clj @@ -1,33 +1,32 @@ (ns methodical.impl.standard (:require [methodical.interface :as i] - [potemkin.types :as p.types] [pretty.core :refer [PrettyPrintable]]) (:import [methodical.interface Dispatcher MethodCombination MethodTable MultiFnImpl])) (defn- ^:static effective-method [^MultiFnImpl impl, dispatch-value] - (or (.effective-method impl dispatch-value) + (or (.effectiveMethod impl dispatch-value) (throw (UnsupportedOperationException. (format "No matching method for dispatch value %s" dispatch-value))))) (defn- ^:static invoke-multifn ([^MultiFnImpl impl] - ((effective-method impl (.dispatch-value ^Dispatcher (.dispatcher impl))))) + ((effective-method impl (.dispatchValue ^Dispatcher (.dispatcher impl))))) ([^MultiFnImpl impl a] - ((effective-method impl (.dispatch-value ^Dispatcher (.dispatcher impl) a)) a)) + ((effective-method impl (.dispatchValue ^Dispatcher (.dispatcher impl) a)) a)) ([^MultiFnImpl impl a b] - ((effective-method impl (.dispatch-value ^Dispatcher (.dispatcher impl) a b)) a b)) + ((effective-method impl (.dispatchValue ^Dispatcher (.dispatcher impl) a b)) a b)) ([^MultiFnImpl impl a b c] - ((effective-method impl (.dispatch-value ^Dispatcher (.dispatcher impl) a b c)) a b c)) + ((effective-method impl (.dispatchValue ^Dispatcher (.dispatcher impl) a b c)) a b c)) ([^MultiFnImpl impl a b c d] - ((effective-method impl (.dispatch-value ^Dispatcher (.dispatcher impl) a b c d)) a b c d)) + ((effective-method impl (.dispatchValue ^Dispatcher (.dispatcher impl) a b c d)) a b c d)) ([^MultiFnImpl impl a b c d & more] - (apply (effective-method impl (.dispatch-value ^Dispatcher (.dispatcher impl) a b c d more)) a b c d more))) + (apply (effective-method impl (.dispatchValue ^Dispatcher (.dispatcher impl) a b c d more)) a b c d more))) -(p.types/deftype+ StandardMultiFn [^MultiFnImpl impl mta] +(deftype StandardMultiFn [^MultiFnImpl impl mta] PrettyPrintable (pretty [_] (list 'multifn impl)) @@ -50,87 +49,87 @@ (StandardMultiFn. impl new-meta))) MethodCombination - (allowed-qualifiers [_] + (allowedQualifiers [_] (i/allowed-qualifiers (i/method-combination impl))) - (combine-methods [_ primary-methods aux-methods] + (combineMethods [_ primary-methods aux-methods] (i/combine-methods (i/method-combination impl) primary-methods aux-methods)) - (transform-fn-tail [_ qualifier fn-tail] + (transformFnTail [_ qualifier fn-tail] (i/transform-fn-tail (i/method-combination impl) qualifier fn-tail)) Dispatcher - (dispatch-value [_] - (.dispatch-value ^Dispatcher (.dispatcher impl))) - (dispatch-value [_ a] - (.dispatch-value ^Dispatcher (.dispatcher impl) a)) - (dispatch-value [_ a b] - (.dispatch-value ^Dispatcher (.dispatcher impl) a b)) - (dispatch-value [_ a b c] - (.dispatch-value ^Dispatcher (.dispatcher impl) a b c)) - (dispatch-value [_ a b c d] - (.dispatch-value ^Dispatcher (.dispatcher impl) a b c d)) - (dispatch-value [_ a b c d more] - (.dispatch-value ^Dispatcher (.dispatcher impl) a b c d more)) - - (matching-primary-methods [_ method-table dispatch-value] + (dispatchValue [_] + (.dispatchValue ^Dispatcher (.dispatcher impl))) + (dispatchValue [_ a] + (.dispatchValue ^Dispatcher (.dispatcher impl) a)) + (dispatchValue [_ a b] + (.dispatchValue ^Dispatcher (.dispatcher impl) a b)) + (dispatchValue [_ a b c] + (.dispatchValue ^Dispatcher (.dispatcher impl) a b c)) + (dispatchValue [_ a b c d] + (.dispatchValue ^Dispatcher (.dispatcher impl) a b c d)) + (dispatchValue [_ a b c d more] + (.dispatchValue ^Dispatcher (.dispatcher impl) a b c d more)) + + (matchingPrimaryMethods [_ method-table dispatch-value] (i/matching-primary-methods (.dispatcher impl) method-table dispatch-value)) - (matching-aux-methods [_ method-table dispatch-value] + (matchingAuxMethods [_ method-table dispatch-value] (i/matching-aux-methods (.dispatcher impl) method-table dispatch-value)) - (default-dispatch-value [_] + (defaultDispatchValue [_] (i/default-dispatch-value (.dispatcher impl))) (prefers [_] (i/prefers (.dispatcher impl))) - (prefer-method [this dispatch-val-x dispatch-val-y] + (preferMethod [this dispatch-val-x dispatch-val-y] (i/with-dispatcher this (i/prefer-method (.dispatcher impl) dispatch-val-x dispatch-val-y))) MethodTable - (primary-methods [_] + (primaryMethods [_] (i/primary-methods (i/method-table impl))) - (aux-methods [_] + (auxMethods [_] (i/aux-methods (i/method-table impl))) - (add-primary-method [this dispatch-val method] + (addPrimaryMethod [this dispatch-val method] (i/with-method-table this (i/add-primary-method (i/method-table impl) dispatch-val method))) - (remove-primary-method [this dispatch-val] + (removePrimaryMethod [this dispatch-val] (i/with-method-table this (i/remove-primary-method (i/method-table impl) dispatch-val))) - (add-aux-method [this qualifier dispatch-val method] + (addAuxMethod [this qualifier dispatch-val method] (i/with-method-table this (i/add-aux-method (i/method-table impl) qualifier dispatch-val method))) - (remove-aux-method [this qualifier dispatch-val method] + (removeAuxMethod [this qualifier dispatch-val method] (i/with-method-table this (i/remove-aux-method (i/method-table impl) qualifier dispatch-val method))) MultiFnImpl - (method-combination [_] + (methodCombination [_] (i/method-combination impl)) (dispatcher [_] (.dispatcher impl)) - (with-dispatcher [this new-dispatcher] + (withDispatcher [this new-dispatcher] (assert (instance? Dispatcher new-dispatcher)) (if (= (.dispatcher impl) new-dispatcher) this (StandardMultiFn. (i/with-dispatcher impl new-dispatcher) mta))) - (method-table [_] + (methodTable [_] (i/method-table impl)) - (with-method-table [this new-method-table] + (withMethodTable [this new-method-table] (assert (instance? MethodTable new-method-table)) (if (= (i/method-table impl) new-method-table) this (StandardMultiFn. (i/with-method-table impl new-method-table) mta))) - (effective-method [_ dispatch-value] - (.effective-method impl dispatch-value)) + (effectiveMethod [_ dispatch-value] + (.effectiveMethod impl dispatch-value)) java.util.concurrent.Callable (call [_] diff --git a/src/methodical/interface.clj b/src/methodical/interface.clj deleted file mode 100644 index d587059..0000000 --- a/src/methodical/interface.clj +++ /dev/null @@ -1,113 +0,0 @@ -(ns methodical.interface - (:refer-clojure :exclude [isa? prefers prefer-method]) - (:require [potemkin.types :as p.types])) - -(p.types/definterface+ MethodCombination - (allowed-qualifiers [method-combination] - "The set containg 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 qualifer, e.g. `:before`, `:after`, or `:around`. - - (allowed-qualifiers (clojure-method-combination)) ;-> #{nil} - (allowed-qualifiers (clos-method-combination)) ;-> #{nil :before :after :around} - (allowed-qualifiers (doseq-method-combination)) ;-> #{:doseq}") - - (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.") - - (transform-fn-tail [method-combination qualifier fn-tail] - "Make appropriate transformations to the `fn-tail` of a `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.)")) - -(p.types/definterface+ MethodTable - (primary-methods [method-table] - "Get a `dispatch-value -> fn` map of all primary methods assoicated with this 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 [method-table dispatch-value f] - "Set the primary method implementation for `dispatch-value`, replacing it if it already exists.") - - (remove-primary-method [method-table dispatch-value] - "Remove the primary method for `dispatch-value`.") - - (add-aux-method [method-table qualifier dispatch-value f] - "Add an auxiliary method implementation for `qualifer` (e.g. `:before`) and `dispatch-value`. Unlike primary - methods, auxiliary methods are not limited to one method per dispatch value; thus this method does not remove - existing methods for this dispatch value. existing ") - - (remove-aux-method [method-table qualifier dispatch-val method] - "Remove an auxiliary method from a method table. Because multiple auxiliary methods are allowed for the same - dispatch value, existing implementations of `MethodTable` are currently only able to remove exact matches -- for - functions, this usually means identical objects. - - In the future, I hope to fix this by storing unique indentifiers in the metadata of methods in the map.")) - -(p.types/definterface+ Dispatcher - (dispatch-value - [dispatcher] - [dispatcher a] - [dispatcher a b] - [dispatcher a b c] - [dispatcher a b c d] - [dispatcher a b c d more] - "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] - "Return a sequence of applicable primary methods for `dispatch-value`, sorted from most-specific to - least-specific. 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] - "Return a map of aux method qualifier -> sequence of applicable methods for `dispatch-value`, sorted from - most-specific to least-specific.") - - (default-dispatch-value [dispatcher] - "Default dispatch value to use if no other dispatch value matches.") - - (prefers [dispatcher] - "Return a map of preferred dispatch value -> set of other dispatch values.") - - (prefer-method [dispatcher dispatch-val-x dispatch-val-y] - "Prefer `dispatch-val-x` over `dispatch-val-y` for dispatch and method combinations.")) - - -(p.types/definterface+ MultiFnImpl - (^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 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.MultiFnImpl 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] - "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 `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.")) - -(p.types/definterface+ Cache - (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 the effective method for `dispatch-value` in this 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.")) diff --git a/src/methodical/interface.cljc b/src/methodical/interface.cljc new file mode 100644 index 0000000..17ae55e --- /dev/null +++ b/src/methodical/interface.cljc @@ -0,0 +1,224 @@ +(ns methodical.interface + (:refer-clojure :exclude [isa? prefers prefer-method])) + +#?(:clj + (defmacro ^:private defonceinterface [interface-name & body] + (let [class-name (clojure.string/replace (str *ns* "." interface-name) #"\-" "_") + exists (try + (Class/forName class-name) + true + (catch Exception _ + false))] + (if exists + `(do + (import ~(symbol class-name)) + nil) + `(definterface ~interface-name ~@body))))) + +#?(:clj + (defonceinterface MethodCombination + (allowedQualifiers []) + (combineMethods [primary-methods aux-methods]) + (transformFnTail [qualifier fn-tail])) + :cljs + (def MethodCombination)) + +(defn allowed-qualifiers + "The set containg 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 qualifer, e.g. `:before`, `:after`, or `:around`. + + (allowed-qualifiers (clojure-method-combination)) ;-> #{nil} + (allowed-qualifiers (clos-method-combination)) ;-> #{nil :before :after :around} + (allowed-qualifiers (doseq-method-combination)) ;-> #{:doseq}" + [^MethodCombination method-combination] + (.allowedQualifiers method-combination)) + +(defn combine-methods + "Combine a sequence of matching `primary-methods` with `aux-methods` (a map of qualifier -> sequence of methods) + into a single effective method." + [^MethodCombination method-combination primary-methods aux-methods] + (.combineMethods method-combination primary-methods aux-methods)) + +(defn transform-fn-tail + "Make appropriate transformations to the `fn-tail` of a `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.)" + [^MethodCombination method-combination qualifier fn-tail] + (.transformFnTail method-combination qualifier fn-tail)) + +#?(:clj + (defonceinterface MethodTable + (primaryMethods []) + (auxMethods []) + (addPrimaryMethod [dispatch-value f]) + (removePrimaryMethod [dispatch-value]) + (addAuxMethod [qualifier dispatch-value f]) + (removeAuxMethod [qualifier dispatch-val method])) + :cljs + (def MethodTable)) + +(defn primary-methods + "Get a `dispatch-value -> fn` map of all primary methods assoicated with this method table." + [^MethodTable method-table] + (.primaryMethods method-table)) + +(defn aux-methods + "Get a `qualifier -> dispatch-value -> [fn]` map of all auxiliary methods associated with this method table." + [^MethodTable method-table] + (.auxMethods method-table)) + +(defn add-primary-method + "Set the primary method implementation for `dispatch-value`, replacing it if it already exists." + [^MethodTable method-table dispatch-value f] + (.addPrimaryMethod method-table dispatch-value f)) + +(defn remove-primary-method + "Remove the primary method for `dispatch-value`." + [^MethodTable method-table dispatch-value] + (.removePrimaryMethod method-table dispatch-value)) + +(defn add-aux-method + "Add an auxiliary method implementation for `qualifer` (e.g. `:before`) and `dispatch-value`. Unlike primary + methods, auxiliary methods are not limited to one method per dispatch value; thus this method does not remove + existing methods for this dispatch value. existing " + [^MethodTable method-table qualifier dispatch-value f] + (.addAuxMethod method-table qualifier dispatch-value f)) + +(defn remove-aux-method + "Remove an auxiliary method from a method table. Because multiple auxiliary methods are allowed for the same + dispatch value, existing implementations of `MethodTable` are currently only able to remove exact matches -- for + functions, this usually means identical objects. + + In the future, I hope to fix this by storing unique indentifiers in the metadata of methods in the map." + [^MethodTable method-table qualifier dispatch-val method] + (.removeAuxMethod method-table qualifier dispatch-val method)) + +#?(:clj + (defonceinterface Dispatcher + (dispatchValue []) + (dispatchValue [a]) + (dispatchValue [a b]) + (dispatchValue [a b c]) + (dispatchValue [a b c d]) + (dispatchValue [a b c d more]) + + (matchingPrimaryMethods [method-table dispatch-value]) + (matchingAuxMethods [method-table dispatch-value]) + (defaultDispatchValue []) + (prefers []) + (preferMethod [dispatch-val-x dispatch-val-y])) + :cljs + (def Dispatcher)) + +(defn dispatch-value + "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.)" + ([^Dispatcher dispatcher] (.dispatchValue dispatcher)) + ([^Dispatcher dispatcher a] (.dispatchValue dispatcher a)) + ([^Dispatcher dispatcher a b] (.dispatchValue dispatcher a b)) + ([^Dispatcher dispatcher a b c] (.dispatchValue dispatcher a b c)) + ([^Dispatcher dispatcher a b c d] (.dispatchValue dispatcher a b c d)) + ([^Dispatcher dispatcher a b c d more] (.dispatchValue dispatcher a b c d more))) + +(defn matching-primary-methods + "Return a sequence of applicable primary methods for `dispatch-value`, sorted from most-specific to + least-specific. 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." + [^Dispatcher dispatcher method-table dispatch-value] + (.matchingPrimaryMethods dispatcher method-table dispatch-value)) + +(defn matching-aux-methods + "Return a map of aux method qualifier -> sequence of applicable methods for `dispatch-value`, sorted from + most-specific to least-specific." + [^Dispatcher dispatcher method-table dispatch-value] + (.matchingAuxMethods dispatcher method-table dispatch-value)) + +(defn default-dispatch-value + "Default dispatch value to use if no other dispatch value matches." + [^Dispatcher dispatcher] + (.defaultDispatchValue dispatcher)) + +(defn prefers + "Return a map of preferred dispatch value -> set of other dispatch values." + [^Dispatcher dispatcher] + (.prefers dispatcher)) + +(defn prefer-method + "Prefer `dispatch-val-x` over `dispatch-val-y` for dispatch and method combinations." + [^Dispatcher dispatcher dispatch-val-x dispatch-val-y] + (.preferMethod dispatcher dispatch-val-x dispatch-val-y)) + +#?(:clj + (defonceinterface MultiFnImpl + (^methodical.interface.MethodCombination methodCombination []) + (^methodical.interface.Dispatcher dispatcher []) + (^methodical.interface.MultiFnImpl withDispatcher [new-dispatcher]) + (^methodical.interface.MethodTable methodTable []) + (^methodical.interface.MultiFnImpl withMethodTable [new-method-table]) + (effectiveMethod [dispatch-value])) + :cljs + (def MultiFnImpl)) + +(defn ^methodical.interface.MethodCombination method-combination + "Get the method combination associated with this multifn." + [^MultiFnImpl multifn] + (.methodCombination multifn)) + +(defn ^methodical.interface.Dispatcher dispatcher + "Get the dispatcher associated with this multifn." + [^MultiFnImpl multifn] + (.dispatcher multifn)) + +(defn ^methodical.interface.MultiFnImpl with-dispatcher + "Return a copy of this multifn using `new-dispatcher` as its dispatcher." + [^MultiFnImpl multifn new-dispatcher] + (.withDispatcher multifn new-dispatcher)) + +(defn ^methodical.interface.MethodTable method-table + "Get the method table associated with this multifn." + [^MultiFnImpl multifn] + (.methodTable multifn)) + +(defn ^methodical.interface.MultiFnImpl with-method-table + "Return a copy of this multifn using `new-method-table` as its method table." + [^MultiFnImpl multifn new-method-table] + (.withMethodTable multifn new-method-table)) + +(defn effective-method + "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 `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." + [^MultiFnImpl multifn dispatch-value] + (.effectiveMethod multifn dispatch-value)) + +#?(:clj + (defonceinterface Cache + (cachedMethod [dispatch-value]) + (cacheMethodBang [dispatch-value method]) + (clearCacheBang []) + (^methodical.interface.Cache emptyCopy [])) + :cljs + (def Cache)) + +(defn cached-method + "Return cached effective method for `dispatch-value`, if it exists in the cache." + [^Cache cache dispatch-value] + (.cachedMethod cache dispatch-value)) + +(defn cache-method! + "Cache the effective method for `dispatch-value` in this cache." + [^Cache cache dispatch-value method] + (.cacheMethodBang cache dispatch-value method)) + +(defn clear-cache! + "Empty the contents of the cache in-place." + [^Cache cache] + (.clearCacheBang cache)) + +(defn ^methodical.interface.Cache empty-copy + "Return an empty copy of the same type as this cache, e.g. for use when copying a multifn." + [^Cache cache] + (.emptyCopy cache)) diff --git a/test/methodical/impl/cache/watching_test.clj b/test/methodical/impl/cache/watching_test.clj index c50ca26..7027269 100644 --- a/test/methodical/impl/cache/watching_test.clj +++ b/test/methodical/impl/cache/watching_test.clj @@ -9,7 +9,7 @@ [& [num-times-cleared]] (reify Cache - (clear-cache! [_] + (clearCacheBang [_] (some-> num-times-cleared (swap! inc))) PrettyPrintable diff --git a/test/methodical/impl/dispatcher/standard_test.clj b/test/methodical/impl/dispatcher/standard_test.clj index 2502da1..4256bfe 100644 --- a/test/methodical/impl/dispatcher/standard_test.clj +++ b/test/methodical/impl/dispatcher/standard_test.clj @@ -15,8 +15,8 @@ (defn- method-table [primary-methods aux-methods] (reify MethodTable - (primary-methods [_] primary-methods) - (aux-methods [_] aux-methods))) + (primaryMethods [_] primary-methods) + (auxMethods [_] aux-methods))) (def ^:private basic-hierarchy (-> (make-hierarchy)