@weavejester FYI: https://github.com/RickMoynihan/redef-methods
I haven’t created an official release yet — currently trialing it out. Any comments greatfully received.
@rickmoynihan Looks pretty reasonable, and a good tool for stubbing out multimethods. When you're happy with it, submit it to the Clojure toolbox and I'll add it to the directory 🙂
cool
my main issue with it, which I still need to investigate more fully is how it interacts with ig/load-namespaces
I haven’t fully debugged it yet… but it looked very much like if the method bodies aren’t loaded before you with-redef-methods
it - then you’d get a classcast exception
I see what you mean... If a defmethod
is hit for an overridden multimethod.
so I had to resort to loading the namespaces by hand, before redef-ing them. I’m guessing because load-namespaces
replaces the var
yeah
I don't think load-namespaces
replaces the var... since the var would be integrant.core/init-key
, which should already be loaded, because load-namespaces
is in integrant.core
.
yeah you’re right — it’s the internals of the defmethod though
But what it will do is call defmethod
on init-key
, so it expects init-key
to be a multimethod.
Okay, so defmethod
expects a clojure.lang.MultiFn
...
not sure quite what you’re saying… what I’m saying is that the internals of a multi are mutable state too
yeah
that was the ClassCastException
not got a repl open just now
was meaning to isolate the causes in a minimal harness
ahh right enough there’s a :tag
hint
So I think I'd copy the multimethod... like, (clojure.lang.MultiFn. (.name m) (.dispatchFn m) (.defaultDispatchVal m) (.hierarchy m))
Since they're all public members: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/MultiFn.java
yeah was just thinking something like that would be the solution
thanks
And then override the copied MultiFn by adding your own .addMethod
.
really? I avoid mutating anything just now
Maybe set up a default dispatch that goes back to the original?
does that already
Even though you'd be mutating the MultiFn clone, you'd be throwing away the clone afterwards.
Like:
(defn clone-multimethod [m]
(let [m' (clojure.lang.MultiFn. (.name m) ...)]
(doseq [[k f] (methods m)]
(.addMethod m' k f))
m'))
Or just add a default dispatch going back to the original, maybe:
(defn clone-multimethod [m]
(let [m' (clojure.lang.MultiFn. (.name m) ...)]
(.addMethod m' (.defaultDispatchVal m) m)
m'))
Although... I guess you'd want a proxy of the MultiFn, so you could ensure that .addMethod
doesn't override the stuff you've overridden yourself.
Perhaps at that point it's better to go back to Java and inherit a new class from the old... unless proxy does that for us. I can't remember if it keeps inherited methods or not.
sorry had to rush off out… will have a look and think