1

I am designing some polymorphic code to perform mathematical operations. The idea is to abstract out the underlying representation of the data, as different use cases require different representations. Operations can only be performed with data of the same type.

There are many algorithms operating on these things and as these algorithms require a lot of operations. Therefore IMO the best design is to create a base interface defining polymorphic operations and have each data type be an implementation of that interface:

interface Datum<D extends Datum<D>> {
    D fuzz();
    D fuse(D that);
    D fizz(int base);
}

class MadDatum extends Datum<BigDatum> {
    ...
}

However, I also need certain constants to be defined for each representation. I would like to access these constants polymorphically so that generic algorithms that are agnostic to the implementation of Datum can cleanly obtain constants of the correct type. As constants don't depend on a Datum, it doesn't really make sense to define them as methods there. But the only reasonable alternative that I can think of is to create a parallel type hierarchy representing the type of data:

interface DataType<D extends Datum<D>> {
    D warpTorsion();
    D meltingAngle();
}

But then methods need to take both the type and the data:

<D extends Datum<D>> D newmansMethod(DataType<D> type, Collection<D> data);

This design is awkward because it duplicates the class hierarchy in a way that the compiler doesn't check - one can easily implement Data and forget to implement a corresponding DataType - and it requires an extra method parameter in possibly hundreds of methods.

I am aware that Haskell admits an elegant solution to this problem because it allows constants / "nullary functions" to be specified in type classes. Does anyone know a clean way to replicate this functionality in Java?

8
  • How can that last line of code work if you forget to implement DataType<D>?
    – user22815
    Commented Jun 8, 2016 at 18:05
  • @Snowman One particular type of Datum, say MadDatum, might not have a corresponding MadDataType, which would mean you couldn't call newmansMethod<MadDatum> , but you would only notice that issue downstream when you actually try to call newmansMethod. Commented Jun 8, 2016 at 18:34
  • @Solomonoff'sSecret What would you want to happen if someone left out one of these methods for a particular data type? Is it expected and just return null? Return a superclass version? Or compiler should force you to write something?
    – Ordous
    Commented Jun 8, 2016 at 19:55
  • @Ordous Ideally it wouldn't be possible to leave them out, just like you have to implement each method in an interface. Commented Jun 8, 2016 at 19:59
  • @Solomonoff'sSecret the approach I am coming from is if it is not possible to call the method without the corresponding type, then how is that an issue? This is a step higher than a compile issue: one cannot even write the code without the necessary types in place. The way I understand this issue, it would take care of itself very quickly during development. This is similar to adding an interface to the class declaration and not adding the methods on that interface to the class. It doesn't work. So, just add the code you need.
    – user22815
    Commented Jun 8, 2016 at 20:06

2 Answers 2

2

If I understood your question correctly, you're looking for abstract static. This is not possible in Java, but here are some workarounds: https://stackoverflow.com/questions/1916019/java-abstract-static-workaround . As an alternative, you could use other languages with allow this, for example Scala

1

You could go all the way with the Haskell style (Haskell implementations can, in the most general case, pass around one invisible argument for each typeclass requirement).

Eliminate the base type Datum and move all the interesting methods related to it onto DataType:

interface DataType<D> {
    D fuzz(D datum);
    D fuse(D datum1, D datum2);
    D fizz(D datum, int base);
    D warpTorsion();
    D meltingAngle();
}

Then you get:

<D> D newmansMethod(DataType<D> type, Collection<D> data);

And because almost everything interesting is in the implementation of DataType, it's very hard to forget to implement it. You could even retrofit existing types that are ignorant of your library to have their own implementations of DataType.

The JITer might be able to speed things up because it can deduce more about the methods that will be called because all the method dispatch is off the same DataType object, but I don't know if that will make a difference in practice.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.