There are other implementation details to deal with (as of Clojure 1.10.3), and I would be prepared to update your implementation as Clojure changes (ie., everything I mention below relies on completely undocumented implementation details relevant to Clojure 1.8-1.11):
1. As mentioned by hiredman, protocol methods are reset by extend
. Here is the related issue. If you want your protocol monkey patch to survive an extend
, you need to alter the :method-builders
value for that protocol. However, there are subtle requirements for propagating .__methodImplCache
to the actual implementation. From a glance, I don't think your code handles this correctly (the inner cache is never updated).
2. Protocol methods are inlined in some cases (end users only really notice this when you see a No single method ...
error when you mess up a protocol call). This means your call (pfoo x)
becomes (.pfoo x)
and your monkey patch is circumvented. Try adding :inline
metadata to the protocol method to avoid this:
;; untested
(-> #'pfoo alter-meta assoc :inline (fn [& args] `((do pfoo) ~@args))
Note that the inlining must be enforced before the Compiler sees any invocations--once it's inlined, it's too late.
I may release a library to help library authors do this kind of thing.