Skip to content

Commit

Permalink
Implement a describe tool (#123)
Browse files Browse the repository at this point in the history
  • Loading branch information
camsaul authored Sep 9, 2022
1 parent 818f8b4 commit 50e2b80
Show file tree
Hide file tree
Showing 30 changed files with 525 additions and 162 deletions.
70 changes: 60 additions & 10 deletions .clj-kondo/config.edn
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,78 @@
["../resources/clj-kondo.exports/methodical/methodical"]

:linters
{:missing-docstring {:level :warning}
:refer {:level :warning}
:unsorted-required-namespaces {:level :warning}
:single-key-in {:level :warning}
:shadowed-var {:level :warning}
:unresolved-symbol {}

:docstring-leading-trailing-whitespace {:level :warning}
{:docstring-leading-trailing-whitespace {:level :warning}
:keyword-binding {:level :warning}
:misplaced-docstring {:level :warning}
:missing-body-in-when {:level :warning}
:missing-docstring {:level :warning}
:missing-else-branch {:level :warning}
:namespace-name-mismatch {:level :warning}
:non-arg-vec-return-type-hint {:level :warning}
:reduce-without-init {:level :warning}
:redundant-fn-wrapper {:level :warning}
:refer {:level :warning}
:shadowed-var {:level :warning}
:single-key-in {:level :warning}
:unsorted-required-namespaces {:level :warning}
:use {:level :warning}
:used-underscored-binding {:level :warning}
:warn-on-reflection {:level :warning}}
:warn-on-reflection {:level :warning}

:unresolved-symbol
{}

:consistent-alias
{:aliases
{clojure.core.protocols clojure.protocols
clojure.datafy datafy
clojure.java.classpath classpath
clojure.java.io io
clojure.math.combinatorics combo
clojure.pprint pprint
clojure.spec.alpha s
clojure.string str
clojure.test t
clojure.tools.namespace.find ns.find
clojure.tools.reader.edn edn
clojure.walk walk
environ.core env
humane-are.core humane-are
methodical.core m
methodical.impl impl
methodical.impl.cache.simple cache.simple
methodical.impl.cache.watching cache.watching
methodical.impl.combo.clojure combo.clojure
methodical.impl.combo.clos combo.clos
methodical.impl.combo.common combo.common
methodical.impl.combo.operator combo.operator
methodical.impl.combo.threaded combo.threaded
methodical.impl.dispatcher.common dispatcher.common
methodical.impl.dispatcher.everything dispatcher.everything
methodical.impl.dispatcher.multi-default dispatcher.multi-default
methodical.impl.dispatcher.standard dispatcher.standard
methodical.impl.method-table.clojure method-table.clojure
methodical.impl.method-table.common method-table.common
methodical.impl.method-table.standard method-table.standard
methodical.impl.multifn.cached multifn.cached
methodical.impl.multifn.standard multifn.standard
methodical.impl.standard impl.standard
methodical.interface i
methodical.macros macros
methodical.util u
methodical.util.describe describe
methodical.util.trace trace
pjstadig.humane-test-output humane-test-output
potemkin p
potemkin.namespaces p.namespaces
potemkin.types p.types
pretty.core pretty
puget.printer puget}}}

:lint-as
{potemkin.types/deftype+ clojure.core/deftype}
{potemkin/defprotocol+ clojure.core/defprotocol
potemkin.types/deftype+ clojure.core/deftype
potemkin.types/defprotocol+ clojure.core/defprotocol}

:skip-comments true

Expand Down
5 changes: 5 additions & 0 deletions src/methodical/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
methodical.interface
methodical.macros
methodical.util
methodical.util.describe
methodical.util.trace
[potemkin :as p]))

Expand All @@ -14,6 +15,7 @@
methodical.impl/keep-me
methodical.interface/keep-me
methodical.macros/keep-me
methodical.util.describe/keep-me
methodical.util.trace/keep-me
methodical.util/keep-me)

Expand Down Expand Up @@ -115,5 +117,8 @@
prefer-method!
with-prefers!]

[methodical.util.describe
describe]

[methodical.util.trace
trace])
7 changes: 6 additions & 1 deletion src/methodical/impl/cache/simple.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
(:require
[clojure.core.protocols :as clojure.protocols]
[methodical.interface]
[methodical.util.describe :as describe]
[pretty.core :as pretty])
(:import
(methodical.interface Cache)))
Expand Down Expand Up @@ -35,4 +36,8 @@
clojure.protocols/Datafiable
(datafy [this]
{:class (class this)
:cache @atomm}))
:cache @atomm})

describe/Describeable
(describe [this]
(format "It caches methods using a %s." (.getCanonicalName (class this)))))
23 changes: 14 additions & 9 deletions src/methodical/impl/cache/watching.clj
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
(ns methodical.impl.cache.watching
"A `Cache` implementation that wraps any other cache, watching one or more references (such as an
atom or var), calling `clear-cache!` whenever one of those references changes.
"A [[methodical.interface/Cache]] implementation that wraps any other cache, watching one or more references (such as an
atom or var), calling [[methodical.interface/clear-cache!]] whenever one of those references changes.
WatchingCaches can be created by calling `add-watches` on another cache. `add-watches` is composable, meaning you
can thread multiple calls to it to build a cache that watches the entire world go by. You could, for example, use
`WatchingCache`s can be created by calling [[add-watches]] on another cache. [[add-watches]] is composable, meaning
you can thread multiple calls to it to build a cache that watches the entire world go by. You could, for example, use
this to build a multifn that supports a dynamic set of hierarchies, letting you add more as you go. The world's your
oyster!
WatchingCaches' watch functions weakly reference their caches, meaning they do not prevent garbage collection of
`WatchingCache`s' watch functions weakly reference their caches, meaning they do not prevent garbage collection of
potentially large method maps; they also automatically clear out their watches when they are garbage collected and
finalized (which, of course, may actually be never -- but worst-case is that some unneeded calls to `clear-cache!`
get made)."
finalized (which, of course, may actually be never -- but worst-case is that some unneeded calls
to [[methodical.interface/clear-cache!]] get made)."
(:require
[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)
Expand Down Expand Up @@ -52,7 +53,11 @@
(datafy [this]
{:class (class this)
:cache (datafy/datafy cache)
:refs refs}))
:refs refs})

describe/Describeable
(describe [this]
(format "It caches methods using a %s." (.getCanonicalName (class this)))))

(defn- cache-watch-fn [cache]
(let [cache-weak-ref (WeakReference. cache)]
Expand Down Expand Up @@ -97,7 +102,7 @@

(defn remove-watches
"Recursively removes all watches from `cache`, and returning the cache it wrapped (in case you want to thread it into
`add-watches` to watch something else). If `cache` is not an instance of `WatchingCache`, returns the cache as-is."
[[add-watches]] to watch something else). If `cache` is not an instance of `WatchingCache`, returns the cache as-is."
[cache]
(if-not (instance? WatchingCache cache)
cache
Expand Down
7 changes: 6 additions & 1 deletion src/methodical/impl/combo/clojure.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
(:require
[clojure.core.protocols :as clojure.protocols]
[methodical.interface]
[methodical.util.describe :as describe]
[pretty.core :as pretty])
(:import
(methodical.interface MethodCombination)))
Expand Down Expand Up @@ -35,4 +36,8 @@

clojure.protocols/Datafiable
(datafy [this]
{:class (class this)}))
{:class (class this)})

describe/Describeable
(describe [this]
(format "It uses the method combination %s." (.getCanonicalName (class this)))))
7 changes: 6 additions & 1 deletion src/methodical/impl/combo/clos.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
[clojure.core.protocols :as clojure.protocols]
[methodical.impl.combo.common :as combo.common]
[methodical.interface]
[methodical.util.describe :as describe]
[pretty.core :as pretty])
(:import
(methodical.interface MethodCombination)))
Expand Down Expand Up @@ -84,4 +85,8 @@

clojure.protocols/Datafiable
(datafy [this]
{:class (class this)}))
{:class (class this)})

describe/Describeable
(describe [this]
(format "It uses the method combination %s." (.getCanonicalName (class this)))))
33 changes: 22 additions & 11 deletions src/methodical/impl/combo/operator.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
"Method combinations strategies based on the non-default method combination types in CLOS. All non-default method
combinations follow the same basic pattern:
(operator (primary-method-1 args)
(primary-method-2 args)
(primary-method-3 args)))
```clj
(operator (primary-method-1 args)
(primary-method-2 args)
(primary-method-3 args)))
```
(Example from \"Object-Oriented Programming in Common Lisp\", Keene 1988.)
Expand All @@ -28,20 +30,23 @@
One last difference: unlike CLOS operator method combinations, primary method implementations *are not* qualified by
their operator.
;; CLOS
(defmethod total-electric-supply + ((city city))
...)
```clj
;; CLOS
(defmethod total-electric-supply + ((city city))
...)
;; Methodical
(defmethod total-electric-supply :city
[city]
...)"
;; Methodical
(defmethod total-electric-supply :city
[city]
...)
```"
(:refer-clojure :exclude [methods])
(:require
[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]
[pretty.core :as pretty])
(:import
(methodical.interface MethodCombination)))
Expand Down Expand Up @@ -195,7 +200,13 @@
clojure.protocols/Datafiable
(datafy [this]
{:class (class this)
:operator operator-name}))
:operator operator-name})

describe/Describeable
(describe [this]
(format "It uses the method combination %s\nwith the operator %s."
(.getCanonicalName (class this))
(pr-str operator-name))))

(defn operator-method-combination
"Create a new method combination using the operator named by `operator-name`, a keyword name of one of the
Expand Down
9 changes: 8 additions & 1 deletion src/methodical/impl/combo/threaded.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[clojure.core.protocols :as clojure.protocols]
[methodical.impl.combo.common :as combo.common]
[methodical.interface]
[methodical.util.describe :as describe]
[pretty.core :as pretty])
(:import
(methodical.interface MethodCombination)))
Expand Down Expand Up @@ -102,7 +103,13 @@
clojure.protocols/Datafiable
(datafy [this]
{:class (class this)
:threading-type threading-type}))
:threading-type threading-type})

describe/Describeable
(describe [this]
(format "It uses the method combination %s\nwith the threading strategy %s."
(.getCanonicalName (class this))
(pr-str threading-type))))

(defn threading-method-combination
"Create a new `ThreadingMethodCombination` using the keyword `threading-type` strategy, e.g. `:thread-first` or
Expand Down
10 changes: 9 additions & 1 deletion src/methodical/impl/dispatcher/everything.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[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)))
Expand Down Expand Up @@ -68,4 +69,11 @@
(datafy [this]
{:class (class this)
:hierarchy hierarchy-var
:prefs prefs}))
:prefs prefs})

describe/Describeable
(describe [this]
(format "It uses the dispatcher %s\nwith hierarchy %s\nand prefs %s."
(.getCanonicalName (class this))
(pr-str hierarchy-var)
(pr-str prefs))))
23 changes: 17 additions & 6 deletions src/methodical/impl/dispatcher/multi_default.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
[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)))
Expand Down Expand Up @@ -44,11 +45,13 @@
"Return a sequence of all partially-specialized default dispatch values for a given `dispatch-value` and
`default-value`, in order from most-specific to least-specific.
(default-dispatch-values [:x :y] :default)
->
([:x :default] ; if no method for [:x :y] exists, look for [:x :default]...
[:default :y] ; or [:default :y] ...
[:default :default])"
```clj
(default-dispatch-values [:x :y] :default)
->
([:x :default] ; if no method for [:x :y] exists, look for [:x :default]...
[:default :y] ; or [:default :y] ...
[:default :default])
```"
[dispatch-value default-value]
(when (and (sequential? dispatch-value)
(not (sequential? default-value)))
Expand Down Expand Up @@ -193,4 +196,12 @@
:dispatch-fn dispatch-fn
:default-value default-value
:hierarchy hierarchy-var
:prefs prefs}))
:prefs prefs})

describe/Describeable
(describe [this]
(format "It uses the dispatcher %s\nwith hierarchy %s\nand prefs %s.\n\nThe default value is %s."
(.getCanonicalName (class this))
(pr-str hierarchy-var)
(pr-str prefs)
(pr-str default-value))))
11 changes: 10 additions & 1 deletion src/methodical/impl/dispatcher/standard.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[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)))
Expand Down Expand Up @@ -163,4 +164,12 @@
:dispatch-fn dispatch-fn
:default-value default-value
:hierarchy hierarchy-var
:prefs prefs}))
:prefs prefs})

describe/Describeable
(describe [this]
(format "It uses the dispatcher %s\nwith hierarchy %s\nand prefs %s.\n\nThe default value is %s."
(.getCanonicalName (class this))
(pr-str hierarchy-var)
(pr-str prefs)
(pr-str default-value))))
Loading

0 comments on commit 50e2b80

Please sign in to comment.