camsaul / methodical

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.
Eclipse Public License 2.0
294 stars 19 forks source link

Macro parsing overhaul #119

Closed camsaul closed 2 years ago

camsaul commented 2 years ago

Resolves #36 Resolves #46 Resolves #109 Resolves #110 Resolves #113

I started by trying to add specs to defmethod and defmulti and realized leveraging those to parse args made way more sense than parsing things the hard way. While writing tests and improving the custom Kondo hook I realized we had a few ambiguous parses for defmethod. So this PR introduces the following new rules for dispatch values:

A dispatch value as parsed to defmethod (i.e., not-yet-evaluated) can be ANYTHING other than the following two things:

  1. an legal aux qualifier for the current method combination, e.g. :after or :around

    It makes the parse for

    (m/defmethod mf :after \"str\" [_])

    ambiguous -- Is this an :after aux method with dispatch value \"str\", or a primary method with dispatch value :after and a docstring? Since there's no clear way to decide which is which, we're going to have to disallow this. It's probably a good thing anyway since you're absolutely going to confuse the hell out of people if you use something like :before or :around as a dispatch value.

  2. A list that can be interpreted as part of a n-arity fn tail i.e. ([args ...] body ...)

    I know, theoretically it should be possible to do something dumb like this:

    (doseq [i    [0 1]
           :let [toucan :toucan pigeon :pigeon]]
     (m/defmethod my-multimethod :before ([toucan pigeon] i)
       ([x]
        ...)))

    but we are just UNFORTUNATELY going to have to throw up our hands and say we don't support it. The reason is in the example above it's ambiguous whether this is a :before aux method with dispatch value ([toucan pigeon] i), or a primary method with dispatch value :before. It's just impossible to tell what you meant. If you really want to do something wacky like this, let-bind the dispatch value to a symbol or something.

Once I sorted that all out it was relatively straightforward to knock out a few other issues at the same time and add docstring support and custom dispatch value validation support.

TODO: Update documentation