I am in the middle of writing some code in Clojure which relies quite heavily on some functionality from the Apache Commons Math3 library. I've never written any Java code before and am trying to come up with an elegant solution to create some clojure wrappers that I frequently use. Hopefully I'll be able to communicate what I'm attempting to do despite not really being an OOP or Java expert. I'll describe the challenge with a concrete simple example
In general I have a collection of java classes which all inherit some methods from a given interface. To give a concrete example, consider the distributions package. There are 3 interfaces, one of which is RealDistribution. Classes which inherit the RealDistribution interface have access to some methods such as "density".
In clojure I can do
(.density (new NormalDistribution 0 1) 2)
(.density (new ExponentialDistribution 1) 2)
The goal is to write a polymorphic function "density" which dispatches on different distributions, themselves java classes.
I could write a protocol, call it density-eval
(defprotocol density-eval
(density [distribution x]))
and extend each distribution manually like
(extend-type NormalDistribution
density-eval
(density [d x] (.density d x)))
However I need to do this for every distribution... and theres a lot of them... and the code is identical for each distribution.
To save time, what I would like to do is extend something more general... say, the parent type of NormalDistribution, but I don't know where to go from here. What I currently have is
(extend-type RealDistribution
density-eval
(density [d x] (.density d x)))
RealDistribution being the interface that NormalDistribution and ExponentialDistribution implement. And this works... I can then call (density y x) for many distributions y that implement the RealDistribution interface. For example (density (new NormalDistribution 0 1) 0) works fine. However, the problem with this is that I play the same game for the classes implementing the IntegerDistribution interface. I define a new protocol
(defprotocol pmf-eval
(pmf [distribution x]))
(extend-type IntegerDistribution
pmf-eval
(pmf [d x] (.probability d x)))
and then for classes that implement the IntegerDistribution interface I can do things like (pmf (new PoissonDistribution 3) 2) and that works out fine... but then I can no longer call (density y x) on distributions y implementing the RealDistribution interface. I get the error:
1. Unhandled java.lang.IllegalArgumentException
No implementation of method: :density of protocol:
#'distributions.core/density-eval found for class:
org.apache.commons.math3.distribution.NormalDistribution
when I execute the second protocol code in the repl somehow it interferes with the first.
Ultimately I want to write polymorphic wrapper functions for the methods of these classes, but I don't know how to do this as simply and as cleanly as possible. Please advise...
What you've written should be fine, although you will have trouble if there are any classes which implement both interfaces.
As to why you say it's not working for you: are you sure? IntegerDistribution in fact has no density argument, and your error message shows you are actually defining a protocol named univariate-integer, not density-eval, so I wonder if you have made an error when trying to simplify and isolate the problem for Stack Overflow.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With