Re: Problem applying generics to my code. Is there a better solution?




Lucas wrote:
How about this:

public static <T, P> Distribution<P> posterior(List<Double> data,
NormalDistribution<T> likelihood, NormalDistribution<P> prior)
{
return normalNormalPosterior(data, likelihood, prior );

}

public static <T, P> Distribution<P> posterior(List<Integer> data,
PoissonDistribution<T> likelihood, GammaDistribution<P> prior)
{
return normalNormalPosterior(data, likelihood, prior);

}

Why have one method that does two things?

Oops. That was a typo on my part. That code path should be

public static <T, P> Distribution<P> posterior(List<Integer> data,
PoissonDistribution<T> likelihood, GammaDistribution<P> prior)
{
return poissonGammaPosterior(data, likelihood, prior);
}

You might even want to make PoissonDistribution implement a method
posterior which takes a List<Integer> data, GammaDistribution<P>
prior), and likewise on the NormalDistribution.

My motivation for the original choice was that, as a user of the
package, I want to manipulate generic Distribution<T> objects and let
the library decide if there is an efficient way of giving a
posterior. There are always numerical methods to fall back on. Also,
there can be multiple conjugate priors for the same likelihood.

Here's an example of how I want to interface to these methods

import static my.package.Conjugate.*;

public static void foo()
{
Distribution<Double> distn1 = new NormalDistribution( mu, sigma );
Distribution<Double> distn2 = new NormalDistribution( 0, tau );

Distribution<Double> post = posterior( distn1, distn2 );
}

So, the underlying problem I'm trying to solve is to dispatch based on
the most specific type of the object. Per your suggestion, I could
get around this by adding a posterior() method that would be
implemented along the lines of

public class NormalDistribution implements Distribution<Double>
{
public Distribution<Double> posterior( List<Double> data,
Distribution<Double> prior )
{
if ( prior instanceof NormalDistibution ) return
normalNormalPosterior( data, this, prior );
if ( prior instanceof GammaDistribution ) return
normalGammaPosterior( data, this, prior );
}
}

Anytime that you find yourself using a chain of "instanceof" checks on
classes which you control, its time to look into using polymorphism
instead... You're particular problem may be best solved through a
Visitor pattern.
Here's an example, I'm removing the generics, as they're not necessary
for my point.

abstract class Distribution {
Distribution posterior(List list, Distribution prior) {
prior.visit(this);
}
protected Distribution posteriorList list, Normal prior) {
throw new IllegalArgumentException("Not valid");
}
protected Distribution posterior(List list, Normal prior) {
throw new IllegalArgumentException("Not valid");
}
public abstract Distribution visit(List list, Distribution d);
}
class Normal {
protected Distribution posterior(List list, Normal prior) {
return normalNormaPosterior(list, this, prior);
}
protected Distribution posterior(List list, Gamma prior) {
return normalGammaPosterior(list, this, prior);
}
public Distribution visit(List list, Distribution d) {
return d.posterior(list, this);
}
}

class Gamma {
protected Distribution posterior(List list, Normal prior) {
return gammaNormaPosterior(list, this, prior);
}
protected Distribution posterior(List list, Gamma prior) {
return gammaGammaPosterior(list, this, prior);
}
public Distribution visit(List list, Distribution d) {
return d.posterior(list, this);
}
}

or should I just create separate methods for each type of supported
prior distribution and force the caller to call the posterior() method
with an concrete type and not the generic Distribution<> type?

That might not be a bad idea, but a "visitor pattern" might be more
appropriate... Alternatively, you might look for algorithms that are
distribution type agnostic. I would probably go for the visitor
approach, google it :-)


I'm going to ramble on a bit here and point out that the library have
a ConditionalDistribution class which would probably be the most
appropriate vehicle for your suggestion since a NormalDistribution
class itself does not have any unbound parameters. To replicate the
last example

public class NormalDistributionWithUnknownMean extends
NormalDistribution implements ConditionalDistribution<Double, Double>
{
public NormalDistribution conjugate( List<Double> data,
NormalDistribution prior )
{
return normalNormalPosterior( data, this, prior );
}
}

public class NormalDistributionWithUnknownVariance extends
NormalDistribution implements ConditionalDistribution<Double, Double>
{
public NormalDistribution conjugate( List<Double> data,
GammaDistribution prior )
{
return normalGammaPosterior( data, this, prior );
}
}

Thank you for the discussion.
I actually don't know much about the problem domain your solving. I'm
guessing statistics? (The one math I haven't studied much) :-)

Anyway, I hope I've been able to help.

.



Relevant Pages