methodical icon indicating copy to clipboard operation
methodical copied to clipboard

Default dispatcher is caching things incorrectly for operator method combinations that return/reduce results

Open camsaul opened this issue 3 years ago • 0 comments

The caching mechanism assumes that only the most-specific primary method will be called. If a :toucan is a :bird and a :toucan is a :can, and we have methods for :bird and :can, and :bird is considered more specific, the cache will incorrectly assume that calling the method for :bird is the same as calling the method for :can. Whichever one is invoked first will result in the other getting wrong values. In the normal one-primary-method-only mode the assumption would hold true, but in that assumption is not true for operator method combinations like seq: calling the method for :bird should only give you the result for :bird, while :toucan should give you the result for both :bird and :can.

I'm not sure about the best way to fix this -- we might need some sort of different type of cache for operator method combinations.

As a workaround for the time being we'll have to use uncached multimethods.

Failing test:

(t/deftest operator-method-combination-caching-tets
  (doseq [ks (clojure.math.combinatorics/permutations [:bird :can :toucan])]
    (t/testing (vec ks)
      (let [mf (-> (m/multifn
                    (m/standard-multifn-impl
                     (m/seq-method-combination)
                     (m/standard-dispatcher
                      keyword
                      :hierarchy (atom (-> (make-hierarchy)
                                           (derive :toucan :can)
                                           (derive :toucan :bird))))
                     (m/standard-method-table)))
                   (m/add-primary-method :bird (constantly {:bird? true}))
                   (m/add-primary-method :can (constantly {:can? true}))
                   (m/prefer-method :bird :can))]
        (doseq [k ks]
          (t/testing k
            (t/is (= (case k
                       :bird   {:bird? true}
                       :can    {:can? true}
                       :toucan {:bird? true, :toucan? true})
                     (reduce merge {} (mf k))))))))))

camsaul avatar Aug 18 '22 02:08 camsaul