GithubHelp home page GithubHelp logo

camsaul / methodical Goto Github PK

View Code? Open in Web Editor NEW
286.0 10.0 16.0 1.16 MB

Functional and flexible multimethods for Clojure. Nondestructive multimethod construction, CLOS-style aux methods and method combinations, partial-default dispatch, easy next-method invocation, helpful debugging tools, and more.

License: Eclipse Public License 2.0

Emacs Lisp 0.34% Clojure 99.23% Shell 0.43%
clojure multimethods common-lisp-object-system clos methodical hacktoberfest

methodical's Introduction

ClojarsDownloads GitHub Workflow Status (branch) License GitHub last commit Codecov GitHub Sponsors cljdoc badge

Clojars Project

New: The Clojure/north 2020 talk is up!

Methodical

Methodical

Methodical is a library that provides drop-in replacements for Clojure multimethods and adds several advanced features.

(require '[methodical.core :as m])

(m/defmulti my-multimethod
  :type)

(m/defmethod my-multimethod Object
  [m]
  (assoc m :object? true))

(my-multimethod {:type Object})
;; -> {:type java.lang.Object, :object? true}

Calling the next-most-specific method with next-method

Inspired by the Common Lisp Object System (CLOS), Methodical methods can call the next-most-specific method, if they should so desire, by calling next-method:

(m/defmethod my-multimethod String
  [m]
  (next-method (assoc m :string? true)))

(my-multimethod {:type String})
;; -> {:type java.lang.String, :string? true, :object? true}

This makes it easy to reuse shared parent implementations of methods without having to know the exact dispatch value of the next method. In vanilla Clojure multimethods, you'd have to do something like this:

((get-method my-multimethod Object) (assoc m :string? true))

If you're not sure whether a next-method exists, you can check whether it's nil before calling it.

Methodical exports custom clj-kondo configuration and hooks for defmulti and defmethod; with the exported configuration it will even tell you if you call next-method with the wrong number of args:

Kondo

Auxiliary Methods: :before, :after, and :around

Inspired by the CLOS, Methodical multimethods support both primary methods and auxiliary methods. Primary methods are the main methods that are invoked for a given dispatch value, such as the implementations for String or Object in the examples above; they are the same type of method vanilla defmethod supports. Auxiliary methods are additional methods that are invoked :before, :after, or :around the primary methods:

(m/defmethod my-multimethod :before String
  [m]
  (assoc m :before? true))

(m/defmethod my-multimethod :around String
  [m]
  (next-method (assoc m :around? true)))

(my-multimethod {:type String})
;; -> {:type java.lang.String, :around? true, :before? true, :string? true, :object? true}

:before methods

All applicable :before methods are invoked before the primary method, in order from most-specific (String before Object) to least-specific. Unlike the CLOS, which ignores the results of :before and :after auxiliary methods, by default Methodical threads the result of each :before method into the next method as its last argument. This better supports Clojure's functional programming style.

(m/defmulti before-example
  (fn [x acc]
    (:type x)))

(m/defmethod before-example :before String
  [x acc]
  (conj acc :string))

(m/defmethod before-example :before Object
  [x acc]
  (conj acc :object))

(m/defmethod before-example :default
  [x acc]
  (conj acc :default))

(before-example {:type String} [])
;; -> [:string :object :default]

:before methods unlock a whole new range of solutions that would be tedious with vanilla Clojure multimethods: suppose you wanted add logging to all invocations of a multimethod. With vanilla multimethods, you'd have to add an individual log statement to every method! With Methodical, just add a new :default :before method:

(m/defmethod my-multimethod :before :default
  [& args]
  (log/debugf "my-multimethod called with args: %s" args)
  ;; return last arg so it is threaded thru for next method
  (last args))

:after methods

All applicable :after methods are invoked after the primary method, in order from least-specific (Object before String) to most-specific. Like :before methods, (by default) the result of the previous method is threaded thru as the last argument of the next function:

(m/defmulti after-example
  (fn [x acc]
    (:type x)))

(m/defmethod after-example :after String
  [x acc]
  (conj acc :string))

(m/defmethod after-example :after Object
  [x acc]
  (conj acc :object))

(m/defmethod after-example :default
  [x acc]
  (conj acc :default))

(after-example {:type String} [])
;; -> [:default :object :string]

An example usecase for :after is chess. When looking up legal moves, you implement how each piece can move, then in :after :default limit it to only spaces on the board.

:around methods

:around methods are called around all other methods and give you the power to choose how or when to invoke those methods, and modify any arguments passed to them, or their result, as needed. Like primary methods (but unlike :before and :after methods), :around methods have an implicit next-method argument; you'll need to call this to invoke the next method. :around methods are invoked from least-specific to most-specific (Object before String):

(m/defmulti around-example
  (fn [x acc]
    (:type x)))

(m/defmethod around-example :around String
  [x acc]
  (as-> acc acc
    (conj acc :string-before)
    (next-method x acc)
    (conj acc :string-after)))

(m/defmethod around-example :around Object
  [x acc]
  (as-> acc acc
    (conj acc :object-before)
    (next-method x acc)
    (conj acc :object-after)))

(m/defmethod around-example :default
  [x acc]
  (conj acc :default))

(around-example {:type String} [])
;; -> [:object-before :string-before :default :string-after :object-after]

Around methods give you amazing power: you can decider whether to skip invoking next-method altogether, or even invoke it more than once; you can acquire resources for the duration of the method invocation with with-open or the like.

Method combinations are discussed more in detail below.

Defining multiple auxiliary methods for the same dispatch value

Unlike primary methods, you can have multiple auxiliary methods for the same dispatch value. However, adding an additional duplicate auxiliary method every time you reload a namespace would be annoying, so the defmethod macro automatically replaces existing auxiliary methods for the same multimethod and dispatch value in the same namespace:

(m/defmulti after-example
  (fn [x acc]
    (:type x)))

(m/defmethod after-example :after String
  [x acc]
  (conj acc :string))

;; replaces the aux method above
(m/defmethod after-example :after String
  [x acc]
  (conj acc :string-2))

(m/defmethod after-example :default
  [x acc]
  (conj acc :default))

(after-example {:type String} [])
;; -> [:default :string-2]

In most cases, this is what you want, and the least-annoying behavior. If you actually do want to define multiple aux methods of the same type for the same multimethod and dispatch value, you can give each method a unique key:

(m/defmulti after-example
  (fn [x acc]
    (:type x)))

(m/defmethod after-example :after String "first String :after method"
  [x acc]
  (conj acc :string))

(m/defmethod after-example :after String "another String :after method"
  [x acc]
  (conj acc :string-2))

(m/defmethod after-example :default
  [x acc]
  (conj acc :default))

(after-example {:type String} [])

;; -> [:default :string-2 :string]

You can also use this key to remove specific auxiliary methods.

Getting the "effective method"

The effective method is the method that is ultimately invoked when you invoke a multimethod for a given dispatch value. With vanilla Clojure multimethods, get-method returns this "effective method" (which is nothing more than a single function); in Methodical, you can use effective-method to build an effective method that combines all auxiliary methods and primary methods into a single composed function. By default, this effective method is cached.

Constructing and composing multimethods programmatically

Perhaps one of the biggest limitations of vanilla multimethods is that they can't be passed around and modified on-the-fly like normal functions or other Clojure datatypes -- they're defined statically by defmulti, and methods can only be added destructively, by altering the original object. Methodical multimethods are implemented entirely as immutable Clojure objects (with the exception of caching).

(let [dispatch-fn :type
      multifn     (-> (m/default-multifn dispatch-fn)
                      (m/add-primary-method Object (fn [next-method m]
                                                     :object)))
      multifn'    (m/add-primary-method multifn String (fn [next-method m]
                                                         :string))]
  ((juxt multifn multifn') {:type String}))

;; -> [:object :string]

Note that when using these programmatic functions, primary and :around methods are each passed an implicit next-method arg as their first arg. The defmethod macro binds this automatically, but you'll need to handle it yourself when using these functions.

Every operation available for Clojure multimethods, and quite a few more, are available with programmatic functions like add-primary-method.

Advanced Customization

Clojure's multimethods, while quite powerful, are somewhat limited in the ways you can customize their behavior. Here's a quick list of some of the things you can do with Methodical multimethods, all of which are simply impossible with vanilla Clojure mulitmethods:

  • Dispatch with multiple hierarchies (e.g., one for each arg)

  • Change the strategy used to cache effective methods (the compiled function that is ultimately invoked for a set of args)

  • Invoke all applicable primary methods, and return a sequence of their results

  • Dynamically compute new primary or auxiliary methods without users manually adding them

  • Support default values for part of a dispatch value, e.g. when dispatching off a pair of classes, e.g. support [String String], [:default String], or [String :default]

  • Combine multiple multimethods into a single multimethod; that, when invoked, tries invoking each constituent multimethod in turn until it finds one with a matching method implementation

To enable such advanced functionality, Methodical multimethods are divided into four components, and two that manage them:

  • The method combination, which defines the way applicable primary and auxiliary methods are combined into a single effective method. The default method combination, thread-last-method-combination, binds implicit next-method args for primary and :around methods, and implements logic to thread the result of each method into the last argument of the next. 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). Method combinations implement the MethodCombination interface.

  • The method table stores primary and auxiliary methods, and returns them when asked. The default implementation, standard-method-table, uses simple Clojure immutable maps, but there is nothing stopping you from creating an implementation that ignores requests to store new methods, or dynamically generates and returns a set of methods based on outside factors. Method tables implement the MethodTable interface.

  • The 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.) The default implementation, multi-default-dispatcher, mostly mimics the behavior of Clojure multimethods, using a dispatch function to determine dispatch values, and a single hierarchy and prefers map to determine which methods are applicable, but supports partial-default methods, e.g, [:default String]. (See this blog post for more information about partial-default dispatch.) You could easily create your own implementation that uses multiple hierarchies, or one that uses no hierarchies at all. Dispatchers implement the Dispatcher interface.

  • A cache, if present, implements a caching strategy for effective methods, so that they need not be recomputed on every invocation. Caches implement the Cache interface. Depending on whether you create a multimethod via defmulti or with the programmatic functions, the cache is either a watching-cache, which watches the hierarchy referenced by the dispatcher (by default, #'clojure.core/global-hierarchy), clearing the cache when it changes; or simple-cache, a bare-bones cache. You could easily implement alternative caching strategies, such as TTL or LRU caches, or ones that better optimize memory and locality.

The method combination, method table, and dispatcher are managed by an object called the multifn impl, which implements MultiFnImpl. If this impl supports caching, it manages a cache as well, albeit indirectly (thru its implementation of the method effective-method.) The default implementation is actually a combination of two multifn impls: cached-multifn-impl manages a cache and wraps standard-multifn-impl, which itself retains the other three components.

Finally, the multifn impl is wrapped in StandardMultiFn, which implements a variety of interfaces, such as clojure.lang.IObj, clojure.lang.Named, clojure.lang.IFn, as well as MethodCombination, MethodTable, Dispatcher, and MultiFnImpl.

You can use alternative components directly in the defmulti macro by passing :combo, :method-table, dispatcher, or :cache:

(m/defmulti custom-multifn
  some-dispatch-fn
  :combo (m/thread-first-method-combination))

When constructing multimethods programmatically, you can use standard-multifn-impl and multifn to create a multimethod with the desired combination of components:

(m/multifn
 (m/standard-multifn-impl
  (m/thread-last-method-combination)
  (m/standard-dispatcher some-dispatch-fn)
  (m/standard-method-table))
 nil
 (m/simple-cache))

Component implementations that ship with Methodical

As previously mentioned, Methodical ships with a variety of alternative implementations of these constituent components of multimethods. The following summarizes all component implementations that currently ship with Methodical:

Method Combinations

  • clojure-method-combination - mimics behavior of vanilla Clojure multimethods. Disallows auxiliary methods; primary methods do not get an implicit next-method arg.

  • clos-method-combination - mimics behavior of the CLOS standard method combination. Supports :before, :after, and :around auxiliary methods. Return values of :before and :after methods are ignored. :after methods are only called with the result of the primary method. Primary and :around methods are given an implicit next-method argument.

  • thread-last-method-combination: the default method combination. Similar to clos-method-combination, but the result of :before methods, the primary method, and :after methods are threaded thru to the next method as the last argument. :after methods are passed the full set of arguments the multimethod as a whole was invoked with.

  • thread-first-method-combination: Like thread-last-method-combination, but results of each method are threaded into the next method as its first arg.

  • Operator method combinations. The following method combinations are inspired by CLOS operator method combinations; each combination behaves similarly, in that it invokes all applicable primary methods, from most-specific to least-specific (String before Object), combining results with the operator for which they are named. Generally, the result is of this form:

    (operator (primary-method-1 args)
              (primary-method-2 args)
              (primary-method-3 args)))

    Operator method combinations support :around methods, but not :before or :after; primary methods do not support next-method, but :around methods do.

    The following operator method combinations ship with Methodical:

    • do-method-combination -- executes all primary methods sequentially, as if by do, returning the result of the least-specific method. The classic use case for this combination is to implement the equivalent of hooks in Emacs Lisp -- you could, for example, define a system shutdown multimethod, and various implementations can be added as needed to to define additional shutdown actions:

      ;; This example uses the `everything-dispatcher`, see below
      ;;
      ;; defmulti always expects a dispatch fn, but since it's not used by the everything dispatcher we can pass
      ;; anything
      (m/defmulti ^:private shutdown!
        :none
        :dispatcher (m/everything-dispatcher)
        :combo (m/do-method-combination))
      
      (m/defmethod shutdown! :task-scheduler
        []
        (println "Shutting down task scheduler..."))
      
      (m/defmethod shutdown! :web-server
        []
        (println "Shutting down web server..."))
      
      (m/prefer-method! #'shutdown! :web-server :task-scheduler)
      
      (m/defmethod shutdown! :around :initiate
        []
        (println "Initiating shutdown...")
        (next-method))
      
      (shutdown!)
      ;; -> Initiating shutdown...
      ;; -> Shutting down web server...
      ;; -> Shutting down task scheduler...
    • min-method-combination -- returns the minimum value returned by all primary methods.

    • max-method-combination -- returns the maximum value returned by all primary methods.

    • +-method-combination -- returns the sum of all values returned by all primary methods. The classic example use case is calculating total electricity usage from a variety of sources.

    • seq-method-combination -- returns a lazy sequence of all values returned by all primary methods.

    • concat-method-combination -- returns a lazy concatenated sequence of all values returned by all primary methods.

      seq-method-combination : map :: concat-method-combination : mapcat
      
    • and-method-combination -- invokes all primary methods until one returns a non-truthy value, at which point it short-circuts.

    • or-method-combination -- invokes all primary methods until one returns a truthy value, at which points it short-circuts and returns that value. You could use this method combination to implement a chain-of-responsibility pattern.

Dispatchers

  • multi-default-dispatcher -- The default. Similar to the behavior of vanilla Clojure multimethods, but also supports "partial default" methods like [String :default] or [:default :some-key]. See this blog post for a more detailed explanation.

  • standard-dispatcher -- Dispatcher that mimics behavior of vanilla Clojure multimethods. Uses a single hierarchy, dispatch function, default dispatch value, and map of preferences defined by prefer-method.

  • everything-dispatcher -- Dispatcher that always considers all primary and auxiliary methods to be matches. Does not calculate dispatch values, but can sort methods from most- to least-specific using a hierarchy and map of preferences. Particularly useful with the operator method combinations.

Method Tables

  • standard-method-table -- The default. A simple method table based on Clojure immutable maps.

  • clojure-method-table -- Like standard-method-table, but disallows auxiliary methods.

Caches

  • simple-cache -- Default for multimethods constructed programmatically. Simple cache that maintains a map of dispatch value -> effective method.

  • watching-cache -- Default for multimethods constructed via defmulti. Wraps another cache (by default, simple-cache) and watches one or more Vars (by default, #'clojure.core/global-hierarchy), clearing the cache when the watched Vars change. Clears watches when cache is garbage-collected.

Multifn Impls

  • standard-multifn-impl -- Basic impl that manages a method combination, dispatcher, and method table.

  • cached-multifn-impl -- wraps another multifn impl and an instance of Cache to implement caching.

Validation

Methodical offers a few opportunities for validation above and beyond what normal Clojure multimethods offer.

:dispatch-value-spec

If you include a :dispatch-value-spec in the metadata of a defmulti, it will automatically be used to validate the dispatch value form of any defmethod forms at macroexpansion time:

(m/defmulti mfx
  {:arglists '([x y]), :dispatch-value-spec (s/cat :x keyword?, :y int?)}
  (fn [x y] [x y]))

(m/defmethod mfx [:x 1]
  [x y]
  {:x x, :y y})
;; => #'methodical.macros-test/mfx

(m/defmethod mfx [:x]
  [x y]
  {:x x, :y y})
;; failed: Insufficient input in: [0] at: [:args-for-method-type :primary :dispatch-value :y] [:x]

This is a great way to make sure people use your multimethods correctly and catch errors right away.

:defmethod-arities

A set of allowed/required arities that defmethod forms are allowed to have. defmethod forms must have arities that match all of the specified :defmethod-arities, and all of its arities must be allowed by :defmethod-arities:

(m/defmulti ^:private mf
  {:arglists '([x]), :defmethod-arities #{1}}
  keyword)

(m/defmethod mf :x [x] x)
;; => ok

(m/defmethod mf :x ([x] x) ([x y] x y))
;; => error: {:arities {:disallowed #{2}}}

(m/defmethod mf :x [x y] x y)
;; => error: {:required #{1}, :disallowed #{2}}

:defmethod-arities must be a set of either integers or [:> n] forms to represent arities with & rest arguments, e.g. [:>= 3] to mean an arity of three or-more arguments:

;; methods must have both a 1-arity and a 3+-arity
(m/defmulti ^:private mf
  {:arglists '([x] [x y z & more]), :defmethod-arities #{1 [:>= 3]}}
  keyword)

(m/defmethod mf :x ([x] x) ([x y z & more] x))
;; => ok

(m/defmethod mf :x [x y] x)
;; => error: {:arities {:required #{1 [:>= 3]}, :disallowed #{2}}}

When rest-argument arities are used, Methodical is smart enough to allow them when appropriate even if they do not specifically match an arity specified in :defmethod-arities:

(m/defmulti ^:private mf
  {:arglists '([x y z & more]), :defmethod-arities #{[:>= 3]}}
  keyword)

(m/defmethod mf :x
  ([a b c] x)
  ([a b c d] x)
  ([a b c d & more] x))
;; => ok, because everything required by [:>= 3] is covered, and everything present is allowed by [:>= 3]

Debugging

Methodical offers debugging facilities so you can see what's going on under the hood, such as the trace utility:

Trace

and the describe utility, which outputs Markdown-formatted documentation, for human-friendly viewing in tools like CIDER:

Describe

This extra information is automatically generated and appended to a multimethod's docstring whenever methods or preferences are added or removed.

Methodical multimethods also implement datafy:

(clojure.datafy/datafy mf)

=>

{:ns           'methodical.datafy-test
 :name         'methodical.datafy-test/mf
 :file         "methodical/datafy_test.clj"
 :line         11
 :column       1
 :arglists     '([x y])
 :class        methodical.impl.standard.StandardMultiFn
 :combo        {:class          methodical.impl.combo.threaded.ThreadingMethodCombination
                :threading-type :thread-last}
 :dispatcher   {:class         methodical.impl.dispatcher.multi_default.MultiDefaultDispatcher
                :dispatch-fn   methodical.datafy-test/dispatch-first
                :default-value :default
                :hierarchy     #'clojure.core/global-hierarchy
                :prefs         {:x #{:y}}}
 :method-table {:class   methodical.impl.method_table.standard.StandardMethodTable
                :primary {:default
                          {:ns       'methodical.datafy-test
                           :name     'methodical.datafy-test/mf-primary-method-default
                           :doc      "Here is a docstring."
                           :file     "methodical/datafy_test.clj"
                           :line     15
                           :column   1
                           :arglists '([next-method x y])}}
                :aux     {:before {[:x :default] [{:ns                    'methodical.datafy-test
                                                   :name                  'methodical.datafy-test/mf-before-method-x-default
                                                   :doc                   "Another docstring."
                                                   :file                  "methodical/datafy_test.clj"
                                                   :column                1
                                                   :line                  20
                                                   :arglists              '([_x y])
                                                   :methodical/unique-key 'methodical.datafy-test}]}
                          :around {[:x :y] [{:ns                    'methodical.datafy-test
                                             :name                  'methodical.datafy-test/mf-around-method-x-y
                                             :file                  "methodical/datafy_test.clj"
                                             :column                1
                                             :line                  25
                                             :arglists              '([next-method x y])
                                             :methodical/unique-key 'methodical.datafy-test}]}}}
 :cache        {:class methodical.impl.cache.watching.WatchingCache
                :cache {:class methodical.impl.cache.simple.SimpleCache
                        :cache {}}
                :refs  #{#'clojure.core/global-hierarchy}}}

Performance

Methodical is built with performance in mind. Although it is written entirely in Clojure, and supports many more features, its performance is similar or better to vanilla Clojure multimethods in many cases. Profiling results with Criterium show Methodical performing up to 20% faster in some cases:

;;; Vanilla clojure
Evaluation count : 1133167380 in 60 samples of 18886123 calls.
             Execution time mean : 43.643309 ns
    Execution time std-deviation : 0.733846 ns
   Execution time lower quantile : 42.421811 ns ( 2.5%)
   Execution time upper quantile : 44.646005 ns (97.5%)
                   Overhead used : 8.836747 ns


;;; Methodical
Evaluation count : 1359687900 in 60 samples of 22661465 calls.
             Execution time mean : 35.327155 ns
    Execution time std-deviation : 0.067655 ns
   Execution time lower quantile : 35.219823 ns ( 2.5%)
   Execution time upper quantile : 35.449303 ns (97.5%)
                   Overhead used : 8.836747 ns

There is still room for even more performance improvement!

License

Code, documentation, and artwork copyright ยฉ 2019-2023 Cam Saul.

Distributed under the Eclipse Public License, same as Clojure.

methodical's People

Contributors

camsaul avatar darkleaf avatar eoliphan avatar escherize avatar jeff303 avatar qnkhuat avatar robhanlon22 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

methodical's Issues

add a is-default[-primary]-method? util fn(s)

We should add a util fn to figure out if the effective method for a dispatch value is the default method. primary-method can tell you if a method exists for that exact dispatch value, and effective-method can give you the method that will be used (similar to get-method for vanilla Multimethods), but there's no way to tell if the effective method is the default method. With vanilla multimethods you can at least check whether the functions are identical?, but that doesn't work in Methodical

Add a dynamic-method-table

Mostly as a proof-of-concept: a MethodTable implementation that can determines which methods it has when you ask it, rather than storing a map. Not sure how this will work, but it would be a good example for others looking to implement advanced behavior

question: next-method-p

I see in the README documentation the function next-method which apparently corresponds to CLOS call-next-method. Is there a function corresponding to CLOS next-method-p ? How can I know programmatically whether it is legal to call next-method ?

"Wrong number of args" ArityExceptions are misleading

If you call a defmethod with the wrong number of args you'll get a misleading arity exception that reports you've passed one more are than you actually have:

(m/defmulti my-multimethod
  (fn [x y]
    [(keyword x) (keyword y)]))

(m/defmethod my-multimethod [:x :y]
  [_])

(my-multimethod :x :y)
;; ->
;; Execution error (ArityException) 
;; Wrong number of args (3) passed to: my-multimethod-primary-method-x-y

This is of course because the defmethod actually macroexpands to

(do
  (defn my-multimethod-primary-method-x-y [next-method _])
  (methodical.util/add-primary-method!
   #'my-multimethod
   [:x :y]
   my-multimethod-primary-method-x-y))

and gets the extra next-method parameter...

instead of macroexpanding to

(defn my-multimethod-primary-method-x-y
  [next-method _]
  ...)

we could do something like

(defn my-multimethod-primary-method-x-y
  [next-method a#]
  ((fn [_]
     ...) a#))

so we get correct error messages. I don't think this would make a noticeable performance difference but probably worth benching just to be sure.

Alternatively just write reify a version of IFn that gives meaningful error messages. Maybe it could even include stuff like the dispatch value

effective-primary-method should return corresponding primary method as-is

It seems like it's actually building a new effective primary method each time you call it.

(m/defmulti multifn
  keyword)

(m/defmethod multifn :default
  [_]
  :default)

(identical? (m/effective-primary-method multifn :y)
            (m/effective-primary-method multifn :y))
;; -> false

Since the primary method doesn't need to be "built" we should just return it as-is.

Related to #61

Add effective-dispatch-value method

See which dispatch value is effectively used for a given set of args. This seems like it could be really useful, but I think it would have to be added to the Dispatcher interface

Error while requiring

Syntax error (ExceptionInfo) compiling at (potemkin.clj:5:67).                                                                                                       
`potemkin.collections/reify-map-type` does not exist   

Followed by subsequent failures from:

#object[clojure.lang.Compiler$CompilerException "0xce492f8" "Syntax error compiling at (methodical/core.clj:1:1)."]

I'm using deps.edn, and here's the resolved tree:

methodical/methodical 0.9.4-alpha
  potemkin/potemkin 0.4.5
    clj-tuple/clj-tuple 0.2.2
  pretty/pretty 1.0.0

m/trace, m/dispatch-value, etc. don't work with more than 4 values

This is of course because the underlying dispatch-value protocol collects the varargs into an args vector when you have more than 4 args. This is an implementation detail however and should not be exposed to the user. We should add a u/dispatch-value function that packs up dispatch values so things work as expected.

Also not working -- ((m/dispatch-fn my-multimethod) a b c d e f) (for similar reasons). We should wrap this function somehow so it works seamlessly without the library user having to know about the underlying protocols

defmacro doesn't properly munge dispatch values with periods

The dispatch value [:default :default :databird.honeysql] results in a method name like update-query*-primary-method-default-default-databird.honeysql which doesn't work since it contains a period. The name generation for defmethod should replace the period with an underscore or something.

Add util fns like dispatch-1 dispatch-2 etc.

Functions that can dispatch on the first n values (optionally passing them to another fn) would be nice. e.g. something like

(dispatch-1 keyword) that would be equivalent to (fn [k & _] (keyword k), but with better per-arity optimizations. I have found myself writing these sorts of functions over and over again, and having a set of util fns for common use cases seems like a win.

`defmulti` should throw an error with invalid args

This code

(m/defmulti multifn
  {:arglists '([x y z])}
  (fn [x y _]
    [(keyword x) (keyword y)]))

;; oops, I should have used defmethod!
(m/defmulti multifn :default
  [x y z]
  :ok)

(multifn :x :y :z)

results in this Exception:

1. Unhandled java.lang.IllegalArgumentException
   Wrong number of args passed to keyword: :default

              Keyword.java:   98  clojure.lang.Keyword/throwArity
              Keyword.java:  149  clojure.lang.Keyword/invoke
              standard.clj:  117  methodical.impl.dispatcher.standard.StandardDispatcher/dispatch_value
              standard.clj:   22  methodical.impl.standard/invoke-multifn
              standard.clj:   30  methodical.impl.standard.StandardMultiFn/invoke

The Exception is only thrown after attempting to invoke the method.

Not sure if this is because the logic to skip redefs is preventing the second defmulti or not, but that should throw an Exception right away instead of when invoking.

New lein project failures

Hello,

Created new lein project with latest Clojure. Added the 0.9.5-alpha-SNAPSHOT as dependency in project.clj. Upon running the REPL (nREPL), I get the following errors output with the error and trace. Hoping you can help me figure out the fix or maybe release a update so I can use this with latest Clojure:

/Library/Java/JavaVirtualMachines/jdk1.8.0_152.jdk/Contents/Home/bin/java -Dfile.encoding=UTF-8 -XX:-OmitStackTraceInFastThrow -Dclojure.compile.path=/Users/samjr/dev/AustinClojure/demo/target/default/classes -Ddemo.version=0.1.0-SNAPSHOT -Dclojure.debug=false "-javaagent:/Users/samjr/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/193.5233.102/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=62437:/Users/samjr/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/193.5233.102/IntelliJ IDEA.app/Contents/bin" -classpath /Users/samjr/dev/AustinClojure/demo/test:/Users/samjr/dev/AustinClojure/demo/src:/Users/samjr/dev/AustinClojure/demo/dev-resources:/Users/samjr/dev/AustinClojure/demo/resources:/Users/samjr/dev/AustinClojure/demo/target/default/classes:/Users/samjr/.m2/repository/nrepl/nrepl/0.6.0/nrepl-0.6.0.jar:/Users/samjr/.m2/repository/clojure-complete/clojure-complete/0.2.5/clojure-complete-0.2.5.jar:/Users/samjr/.m2/repository/methodical/methodical/0.9.5-alpha-SNAPSHOT/methodical-0.9.5-alpha-SNAPSHOT.jar:/Users/samjr/.m2/repository/org/clojure/clojure/1.10.0/clojure-1.10.0.jar:/Users/samjr/.m2/repository/pretty/pretty/1.0.0/pretty-1.0.0.jar:/Users/samjr/.m2/repository/clj-tuple/clj-tuple/0.2.2/clj-tuple-0.2.2.jar:/Users/samjr/.m2/repository/potemkin/potemkin/0.4.5/potemkin-0.4.5.jar:/Users/samjr/.m2/repository/riddley/riddley/0.1.12/riddley-0.1.12.jar:/Users/samjr/.m2/repository/org/clojure/core.specs.alpha/0.2.44/core.specs.alpha-0.2.44.jar:/Users/samjr/.m2/repository/org/clojure/spec.alpha/0.2.176/spec.alpha-0.2.176.jar clojure.main -i /private/var/folders/tq/m0vc_l291jnbb1ny1d1qhv9w0000gn/T/form-init12050333071923363607.clj
#error {
:cause clojure.core$seq_QMARK___5406
:via
[{:type clojure.lang.Compiler$CompilerException
:message Syntax error compiling at (potemkin/collections.clj:28:1).
:data #:clojure.error{:phase :compile-syntax-check, :line 28, :column 1, :source potemkin/collections.clj}
:at [clojure.lang.Compiler load Compiler.java 7647]}
{:type java.lang.NoClassDefFoundError
:message clojure/core$seq_QMARK___5406
:at [potemkin.walk$walk invokeStatic walk.clj 8]}
{:type java.lang.ClassNotFoundException
:message clojure.core$seq_QMARK___5406
:at [java.net.URLClassLoader findClass URLClassLoader.java 381]}]
:trace
[[java.net.URLClassLoader findClass URLClassLoader.java 381]
[java.lang.ClassLoader loadClass ClassLoader.java 424]
[sun.misc.Launcher$AppClassLoader loadClass Launcher.java 338]
[java.lang.ClassLoader loadClass ClassLoader.java 357]
[potemkin.walk$walk invokeStatic walk.clj 8]
[potemkin.walk$postwalk invokeStatic walk.clj 20]
[potemkin.macros$unify_gensyms invokeStatic macros.clj 29]
[potemkin.macros$unify_gensyms invoke macros.clj 29]
[potemkin.collections$eval577 invokeStatic collections.clj 29]
[potemkin.collections$eval577 invoke collections.clj 28]
[clojure.lang.Compiler eval Compiler.java 7176]
[clojure.lang.Compiler load Compiler.java 7635]
[clojure.lang.RT loadResourceScript RT.java 381]
[clojure.lang.RT loadResourceScript RT.java 372]
[clojure.lang.RT load RT.java 463]
[clojure.lang.RT load RT.java 428]
[clojure.core$load$fn__6824 invoke core.clj 6126]
[clojure.core$load invokeStatic core.clj 6125]
[clojure.core$load doInvoke core.clj 6109]
[clojure.lang.RestFn invoke RestFn.java 408]
[clojure.core$load_one invokeStatic core.clj 5908]
[clojure.core$load_one invoke core.clj 5903]
[clojure.core$load_lib$fn__6765 invoke core.clj 5948]
[clojure.core$load_lib invokeStatic core.clj 5947]
[clojure.core$load_lib doInvoke core.clj 5928]
[clojure.lang.RestFn applyTo RestFn.java 142]
[clojure.core$apply invokeStatic core.clj 667]
[clojure.core$load_libs invokeStatic core.clj 5989]
[clojure.core$load_libs doInvoke core.clj 5969]
[clojure.lang.RestFn applyTo RestFn.java 137]
[clojure.core$apply invokeStatic core.clj 667]
[clojure.core$require invokeStatic core.clj 6007]
[clojure.core$require doInvoke core.clj 6007]
[clojure.lang.RestFn invoke RestFn.java 408]
[potemkin$eval163$loading__6706__auto____164 invoke potemkin.clj 1]
[potemkin$eval163 invokeStatic potemkin.clj 1]
[potemkin$eval163 invoke potemkin.clj 1]
[clojure.lang.Compiler eval Compiler.java 7176]
[clojure.lang.Compiler eval Compiler.java 7165]
[clojure.lang.Compiler load Compiler.java 7635]
[clojure.lang.RT loadResourceScript RT.java 381]
[clojure.lang.RT loadResourceScript RT.java 372]
[clojure.lang.RT load RT.java 463]
[clojure.lang.RT load RT.java 428]
[clojure.core$load$fn__6824 invoke core.clj 6126]
[clojure.core$load invokeStatic core.clj 6125]
[clojure.core$load doInvoke core.clj 6109]
[clojure.lang.RestFn invoke RestFn.java 408]
[clojure.core$load_one invokeStatic core.clj 5908]
[clojure.core$load_one invoke core.clj 5903]
[clojure.core$load_lib$fn__6765 invoke core.clj 5948]
[clojure.core$load_lib invokeStatic core.clj 5947]
[clojure.core$load_lib doInvoke core.clj 5928]
[clojure.lang.RestFn applyTo RestFn.java 142]
[clojure.core$apply invokeStatic core.clj 667]
[clojure.core$load_libs invokeStatic core.clj 5985]
[clojure.core$load_libs doInvoke core.clj 5969]
[clojure.lang.RestFn applyTo RestFn.java 137]
[clojure.core$apply invokeStatic core.clj 667]
[clojure.core$require invokeStatic core.clj 6007]
[clojure.core$require doInvoke core.clj 6007]
[clojure.lang.RestFn invoke RestFn.java 408]
[methodical.core$eval157$loading__6706__auto____158 invoke core.clj 1]
[methodical.core$eval157 invokeStatic core.clj 1]
[methodical.core$eval157 invoke core.clj 1]
[clojure.lang.Compiler eval Compiler.java 7176]
[clojure.lang.Compiler eval Compiler.java 7165]
[clojure.lang.Compiler load Compiler.java 7635]
[clojure.lang.RT loadResourceScript RT.java 381]
[clojure.lang.RT loadResourceScript RT.java 372]
[clojure.lang.RT load RT.java 463]
[clojure.lang.RT load RT.java 428]
[clojure.core$load$fn__6824 invoke core.clj 6126]
[clojure.core$load invokeStatic core.clj 6125]
[clojure.core$load doInvoke core.clj 6109]
[clojure.lang.RestFn invoke RestFn.java 408]
[clojure.core$load_one invokeStatic core.clj 5908]
[clojure.core$load_one invoke core.clj 5903]
[clojure.core$load_lib$fn__6765 invoke core.clj 5948]
[clojure.core$load_lib invokeStatic core.clj 5947]
[clojure.core$load_lib doInvoke core.clj 5928]
[clojure.lang.RestFn applyTo RestFn.java 142]
[clojure.core$apply invokeStatic core.clj 667]
[clojure.core$load_libs invokeStatic core.clj 5985]
[clojure.core$load_libs doInvoke core.clj 5969]
[clojure.lang.RestFn applyTo RestFn.java 137]
[clojure.core$apply invokeStatic core.clj 667]
[clojure.core$require invokeStatic core.clj 6007]
[clojure.core$require doInvoke core.clj 6007]
[clojure.lang.RestFn invoke RestFn.java 408]
[demo.core$eval149$loading__6706__auto____150 invoke core.clj 1]
[demo.core$eval149 invokeStatic core.clj 1]
[demo.core$eval149 invoke core.clj 1]
[clojure.lang.Compiler eval Compiler.java 7176]
[clojure.lang.Compiler eval Compiler.java 7165]
[clojure.lang.Compiler load Compiler.java 7635]
[clojure.lang.RT loadResourceScript RT.java 381]
[clojure.lang.RT loadResourceScript RT.java 372]
[clojure.lang.RT load RT.java 463]
[clojure.lang.RT load RT.java 428]
[clojure.core$load$fn__6824 invoke core.clj 6126]
[clojure.core$load invokeStatic core.clj 6125]
[clojure.core$load doInvoke core.clj 6109]
[clojure.lang.RestFn invoke RestFn.java 408]
[clojure.core$load_one invokeStatic core.clj 5908]
[clojure.core$load_one invoke core.clj 5903]
[clojure.core$load_lib$fn__6765 invoke core.clj 5948]
[clojure.core$load_lib invokeStatic core.clj 5947]
[clojure.core$load_lib doInvoke core.clj 5928]
[clojure.lang.RestFn applyTo RestFn.java 142]
[clojure.core$apply invokeStatic core.clj 667]
[clojure.core$load_libs invokeStatic core.clj 5985]
[clojure.core$load_libs doInvoke core.clj 5969]
[clojure.lang.RestFn applyTo RestFn.java 137]
[clojure.core$apply invokeStatic core.clj 667]
[clojure.core$require invokeStatic core.clj 6007]
[clojure.core$require doInvoke core.clj 6007]
[clojure.lang.RestFn invoke RestFn.java 408]
[user$eval5 invokeStatic form-init12050333071923363607.clj 1]
[user$eval5 invoke form-init12050333071923363607.clj 1]
[clojure.lang.Compiler eval Compiler.java 7176]
[clojure.lang.Compiler eval Compiler.java 7165]
[clojure.lang.Compiler eval Compiler.java 7165]
[clojure.lang.Compiler load Compiler.java 7635]
[clojure.lang.Compiler loadFile Compiler.java 7573]
[clojure.main$load_script invokeStatic main.clj 452]
[clojure.main$init_opt invokeStatic main.clj 454]
[clojure.main$init_opt invoke main.clj 454]
[clojure.main$initialize invokeStatic main.clj 485]
[clojure.main$null_opt invokeStatic main.clj 519]
[clojure.main$null_opt invoke main.clj 516]
[clojure.main$main invokeStatic main.clj 598]
[clojure.main$main doInvoke main.clj 561]
[clojure.lang.RestFn applyTo RestFn.java 137]
[clojure.lang.Var applyTo Var.java 705]
[clojure.main main main.java 37]]}
nREPL server started on port 62474 on host 127.0.0.1 - nrepl://127.0.0.1:62474
Connecting to local nREPL server...
Clojure 1.10.0

WeakReference in WatchingCache

Is it possible remove WeakReference usage or add generic WatchingCache?
JavaScript engine does not implement WeakReference but implement WeakMap.

For clojurescript support #20

question: user defined method combination

I'm interested in using methodical to implement a particular method combination.
Is the API already in place for a user to implement his own method combination?
Or is every user expected to use only the method combinations provided by the library release?

Replace watching-cache with Clojure-style non-watching cache

Vanilla Clojure multimethods cache the last known state of their hierarchy when adding methods or during method calculation. They check for changes to the hierarchy on invocation by checking whether the cached hierarchy is the same object as the hierarchy obtained by dereffing the hierarchy IRef.

Methodical multimethods instead put a watch on the hierarchy IRef and wipe the cache when the hierarchy changes. Overall this approach is theoretically slightly faster on method invocation since it avoids the deref and conditional. IRL this probably doesn't make a noticeable difference, and multimethods are always going to be slower than plain functions in tight loops anyway. It would be slower overall however if the hierarchy is changing a lot and the watch is getting triggered all the time when the multimethod is invoked only rarely. Or if you have hundreds of Methodical mulitmethods all watching the global hierarchy and any change to it triggers hundreds watches to reset their caches.

So it seems like which implementation is better depends on access patterns. The watching cache is an overall hairier approach. It also makes ClojureScript support difficult, since the watches use a WeakReference to the multimethod so as to avoid causing them to be retained if the multimethod itself would otherwise be GC'ed (e.g., for a programatically-created multimethod). ClojureScript (AFAIK) doesn't have a direct equivalent to WeakReference -- see #28.

Since the watching cache is more complicated, makes CLJS support hard, and probably not noticeably faster (or possibly slower IRL when you take into account the cost of all those watches), I think we should replace it with a Clojure-style identity-comparison cache.

It might be worth profiling both caches and seeing what performance looks like just be be sure. If the Clojure-style cache performs reasonably similar let's make it the new default and get rid of the watching cache altogether (or at least make it Clojure-only)

Add SBCL-style trace method for method combinations

SBCL lets you trace method combinations:

(defgeneric foo (x)
  (:method (x) 3))
(defmethod foo :around ((x fixnum))
  (1+ (call-next-method)))
(defmethod foo ((x integer))
  (* 2 (call-next-method)))
(defmethod foo ((x float))
  (* 3 (call-next-method)))
(defmethod foo :before ((x single-float))
  'single)
(defmethod foo :after ((x double-float))
 'double)

(trace foo :methods t)

(foo 2.0d0)
  0: (FOO 2.0d0)
    1: ((SB-PCL::COMBINED-METHOD FOO) 2.0d0)
      2: ((METHOD FOO (FLOAT)) 2.0d0)
        3: ((METHOD FOO (T)) 2.0d0)
        3: (METHOD FOO (T)) returned 3
      2: (METHOD FOO (FLOAT)) returned 9
      2: ((METHOD FOO :AFTER (DOUBLE-FLOAT)) 2.0d0)
      2: (METHOD FOO :AFTER (DOUBLE-FLOAT)) returned DOUBLE
    1: (SB-PCL::COMBINED-METHOD FOO) returned 9
  0: FOO returned 9
9

We should add debugging facilities to Methodical to add the same functionality

methodical.util/prefers? uses different logic from actual logic in methodical.impl.dispatcher.common/dominates?

With vanilla Clojure multimethods, preferences are not transitive -- see https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/MultiFn.java#L105. The actual implementation used in methodical.impl.dispatcher.common is not transitive, so the util function shouldn't be either.

I think we should go ahead and remove the util function because prefers? actually needs to look for ancestors in the hierarchy, which is not part of the Dispatcher interface -- see #22.

Can we subclass clojure.lang.MultiFn?

So the usual functions like clojure.core/defmethod and the like can be used. This will make Methodical a true drop-in replacement.

Need to figure out how this will work since clojure.lang.MultiFn is a mutable object and .addMethod actually changes its state, while Methodical multimethods are immutable, so you can only add methods and the like with alter-var-root or equivalent.

Not sure it would actually be possible without reworking Methodical multimethods so they are mutable objects

Implement reversed-method-combination that reverses order methods are applied

CLOS supports :most-specific-last options for the operator method combinations. Here's an example from Practical Common Lisp:

(defgeneric priority (job)
  (:documentation "Return the priority at which the job should be run.")
  (:method-combination + :most-specific-last))  

I think we can do one better by adding a new reversed-method-combination that wraps any other method combination and reverses the matching primary & aux methods before passing to the combo it wraps. Then methods could be reversed for any method combination.

Bonus: reversing a ReversedMethodCombination should return the original wrapped method combination rather than wrapping it again

Reflection warnings on "+" operator

operator.clj:149 recur arg for primitive local: sum is not matching primitive, had: Object, needed: long
Auto-boxing loop arg: sum

Is this patch correct?

(defoperator + [methods invoke]
  (loop [sum 0, [method & more] methods]
    ;;  (let [sum (long (+ (invoke method) sum))] 
    ;;             ^^^^
    (let [sum (+ (invoke method) sum)]
      (if (seq more)
        (recur sum more)
        sum))))

Support an args-xform option

One of the things that has been on my wishlist for a while is to add support for a function that transforms args before dispatch value is calculated, and that passes those args to the effective method. This would let you do things like make an arg optional without requiring all method implementations to support multiple arities. For example, with Metabase's driver methods, we could make the driver arg option, defaulting to *driver* (since this is what you ultimately end up passing in most of the time anyway):

(defn args-xform
  ([f database]
   (f *driver* database))
  ([f driver database]
   (f driver database)))

methodological 0.10.0 not error compilation on repl

This is similar to the first comment ion the following issue but yet id differs:
#33

I am trying to run methodical 0.10.0-alpha with java 1.8 and Clojure 1.10.0 and I get:

error in process sentinel: Could not start nREPL server: Compiling af-river.api.http
Unexpected error macroexpanding p.types/deftype+ at (methodical/impl/cache/simple.clj:9:1).
Exception in thread "main" Unexpected error macroexpanding p.types/deftype+ at (methodical/impl/cache/simple.clj:9:1).
at clojure.lang.Compiler.macroexpand1(Compiler.java:7018)
at clojure.lang.Compiler.macroexpand(Compiler.java:7074)
at clojure.lang.Compiler.compile1(Compiler.java:7715)
at clojure.lang.Compiler.compile(Compiler.java:7797)
at clojure.lang.RT.compile(RT.java:415)
at clojure.lang.RT.load(RT.java:461)
at clojure.lang.RT.load(RT.java:428)
at clojure.core$load$fn__6824.invoke(core.clj:6126)
at clojure.core$load.invokeStatic(core.clj:6125)
at clojure.core$load.doInvoke(core.clj:6109)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5908)
at clojure.core$load_one.invoke(core.clj:5903)
at clojure.core$load_lib$fn__6765.invoke(core.clj:5948)
at clojure.core$load_lib.invokeStatic(core.clj:5947)
at clojure.core$load_lib.doInvoke(core.clj:5928)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$load_libs.invokeStatic(core.clj:5989)
at clojure.core$load_libs.doInvoke(core.clj:5969)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$require.invokeStatic(core.clj:6007)
at clojure.core$require.doInvoke(core.clj:6007)
at clojure.lang.RestFn.invoke(RestFn.java:512)
at methodical.impl$loading__6706__auto____389.invoke(impl.clj:1)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3702)
at clojure.lang.Compiler.compile1(Compiler.java:7730)
at clojure.lang.Compiler.compile1(Compiler.java:7720)
at clojure.lang.Compiler.compile(Compiler.java:7797)
at clojure.lang.RT.compile(RT.java:415)
at clojure.lang.RT.load(RT.java:461)
at clojure.lang.RT.load(RT.java:428)
at clojure.core$load$fn__6824.invoke(core.clj:6126)
at clojure.core$load.invokeStatic(core.clj:6125)
at clojure.core$load.doInvoke(core.clj:6109)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5908)
at clojure.core$load_one.invoke(core.clj:5903)
at clojure.core$load_lib$fn__6765.invoke(core.clj:5948)
at clojure.core$load_lib.invokeStatic(core.clj:5947)
at clojure.core$load_lib.doInvoke(core.clj:5928)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$load_libs.invokeStatic(core.clj:5989)
at clojure.core$load_libs.doInvoke(core.clj:5969)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$require.invokeStatic(core.clj:6007)
at clojure.core$require.doInvoke(core.clj:6007)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at methodical.core$loading__6706__auto____387.invoke(core.clj:1)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3702)
at clojure.lang.Compiler.compile1(Compiler.java:7730)
at clojure.lang.Compiler.compile1(Compiler.java:7720)
at clojure.lang.Compiler.compile(Compiler.java:7797)
at clojure.lang.RT.compile(RT.java:415)
at clojure.lang.RT.load(RT.java:461)
at clojure.lang.RT.load(RT.java:428)
at clojure.core$load$fn__6824.invoke(core.clj:6126)
at clojure.core$load.invokeStatic(core.clj:6125)
at clojure.core$load.doInvoke(core.clj:6109)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5908)
at clojure.core$load_one.invoke(core.clj:5903)
at clojure.core$load_lib$fn__6765.invoke(core.clj:5948)
at clojure.core$load_lib.invokeStatic(core.clj:5947)
at clojure.core$load_lib.doInvoke(core.clj:5928)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$load_libs.invokeStatic(core.clj:5985)
at clojure.core$load_libs.doInvoke(core.clj:5969)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$require.invokeStatic(core.clj:6007)
at clojure.core$require.doInvoke(core.clj:6007)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at af_river.block$loading__6706__auto____385.invoke(block.clj:1)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3702)
at clojure.lang.Compiler.compile1(Compiler.java:7730)
at clojure.lang.Compiler.compile1(Compiler.java:7720)
at clojure.lang.Compiler.compile(Compiler.java:7797)
at clojure.lang.RT.compile(RT.java:415)
at clojure.lang.RT.load(RT.java:461)
at clojure.lang.RT.load(RT.java:428)
at clojure.core$load$fn__6824.invoke(core.clj:6126)
at clojure.core$load.invokeStatic(core.clj:6125)
at clojure.core$load.doInvoke(core.clj:6109)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5908)
at clojure.core$load_one.invoke(core.clj:5903)
at clojure.core$load_lib$fn__6765.invoke(core.clj:5948)
at clojure.core$load_lib.invokeStatic(core.clj:5947)
at clojure.core$load_lib.doInvoke(core.clj:5928)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$load_libs.invokeStatic(core.clj:5985)
at clojure.core$load_libs.doInvoke(core.clj:5969)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$require.invokeStatic(core.clj:6007)
at clojure.core$require.doInvoke(core.clj:6007)
at clojure.lang.RestFn.invoke(RestFn.java:619)
at af_river.schema$loading__6706__auto____383.invoke(schema.clj:1)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3702)
at clojure.lang.Compiler.compile1(Compiler.java:7730)
at clojure.lang.Compiler.compile1(Compiler.java:7720)
at clojure.lang.Compiler.compile(Compiler.java:7797)
at clojure.lang.RT.compile(RT.java:415)
at clojure.lang.RT.load(RT.java:461)
at clojure.lang.RT.load(RT.java:428)
at clojure.core$load$fn__6824.invoke(core.clj:6126)
at clojure.core$load.invokeStatic(core.clj:6125)
at clojure.core$load.doInvoke(core.clj:6109)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5908)
at clojure.core$load_one.invoke(core.clj:5903)
at clojure.core$load_lib$fn__6765.invoke(core.clj:5948)
at clojure.core$load_lib.invokeStatic(core.clj:5947)
at clojure.core$load_lib.doInvoke(core.clj:5928)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$load_libs.invokeStatic(core.clj:5985)
at clojure.core$load_libs.doInvoke(core.clj:5969)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$require.invokeStatic(core.clj:6007)
at clojure.core$require.doInvoke(core.clj:6007)
at clojure.lang.RestFn.invoke(RestFn.java:1523)
at af_river.domain.lookup$loading__6706__auto____178.invoke(lookup.clj:1)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3702)
at clojure.lang.Compiler.compile1(Compiler.java:7730)
at clojure.lang.Compiler.compile1(Compiler.java:7720)
at clojure.lang.Compiler.compile(Compiler.java:7797)
at clojure.lang.RT.compile(RT.java:415)
at clojure.lang.RT.load(RT.java:461)
at clojure.lang.RT.load(RT.java:428)
at clojure.core$load$fn__6824.invoke(core.clj:6126)
at clojure.core$load.invokeStatic(core.clj:6125)
at clojure.core$load.doInvoke(core.clj:6109)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5908)
at clojure.core$load_one.invoke(core.clj:5903)
at clojure.core$load_lib$fn__6765.invoke(core.clj:5948)
at clojure.core$load_lib.invokeStatic(core.clj:5947)
at clojure.core$load_lib.doInvoke(core.clj:5928)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$load_libs.invokeStatic(core.clj:5985)
at clojure.core$load_libs.doInvoke(core.clj:5969)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$require.invokeStatic(core.clj:6007)
at clojure.core$require.doInvoke(core.clj:6007)
at clojure.lang.RestFn.invoke(RestFn.java:3894)
at af_river.api.http$loading__6706__auto____171.invoke(http.clj:1)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3702)
at clojure.lang.Compiler.compile1(Compiler.java:7730)
at clojure.lang.Compiler.compile1(Compiler.java:7720)
at clojure.lang.Compiler.compile(Compiler.java:7797)
at clojure.lang.RT.compile(RT.java:415)
at clojure.lang.RT.load(RT.java:461)
at clojure.lang.RT.load(RT.java:428)
at clojure.core$load$fn__6824.invoke(core.clj:6126)
at clojure.core$load.invokeStatic(core.clj:6125)
at clojure.core$load.doInvoke(core.clj:6109)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5908)
at clojure.core$compile$fn__6829.invoke(core.clj:6136)
at clojure.core$compile.invokeStatic(core.clj:6136)
at clojure.core$compile.invoke(core.clj:6128)
at user$eval155$fn__164.invoke(form-init5573748726313510021.clj:1)
at user$eval155.invokeStatic(form-init5573748726313510021.clj:1)
at user$eval155.invoke(form-init5573748726313510021.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:7176)
at clojure.lang.Compiler.eval(Compiler.java:7166)
at clojure.lang.Compiler.eval(Compiler.java:7166)
at clojure.lang.Compiler.load(Compiler.java:7635)
at clojure.lang.Compiler.loadFile(Compiler.java:7573)
at clojure.main$load_script.invokeStatic(main.clj:452)
at clojure.main$init_opt.invokeStatic(main.clj:454)
at clojure.main$init_opt.invoke(main.clj:454)
at clojure.main$initialize.invokeStatic(main.clj:485)
at clojure.main$null_opt.invokeStatic(main.clj:519)
at clojure.main$null_opt.invoke(main.clj:516)
at clojure.main$main.invokeStatic(main.clj:598)
at clojure.main$main.doInvoke(main.clj:561)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.lang.Var.applyTo(Var.java:705)
at clojure.main.main(main.java:37)
Caused by: java.lang.NoClassDefFoundError: clojure/core$seq_QMARK___5406
at potemkin.types$clean_deftype.invokeStatic(types.clj:72)
at potemkin.types$deftype_PLUS_.invokeStatic(types.clj:291)
at potemkin.types$deftype_PLUS_.doInvoke(types.clj:291)
at clojure.lang.RestFn.applyTo(RestFn.java:151)
at clojure.lang.Var.applyTo(Var.java:705)
at clojure.lang.Compiler.macroexpand1(Compiler.java:6992)
... 201 more
Caused by: java.lang.ClassNotFoundException: clojure.core$seq_QMARK___5406
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 207 more
Compilation failed: Subprocess failed

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.