I want to define a Functor class in Java. This works:
//a Function
public interface F {
public R apply(A a);
}
public interface Functor {
Does anyone still use Java and ponder this problem? You might find this useful...
I've been pondering this for a looooong time. I believe I've made something satisfactory. What I would really like to is indeeed impossible in Java.
This is ideal:
interface Functor extends Functor> {
CONCRETE fmap(Func);
}
Unfortunately, this is make-believe syntax. This kind of thing is possible in C++ with template-template parameters, but not Java.
I was tempted to write this simple thing:
interface Functor {
Functor fmap(Func);
}
This works in some cases, because an implementation can return a covariant return type (for example, List could return a List from this function), but it breaks down when you try passing around generic variables of type "F extends Functor", or a subclass of Functor, etc...
What I ended up doing was introduce a "dummy type variable", like so:
interface Functor {
Functor fmap(Func);
}
The "concrete type" should be the type itself, or some dummy type that guarantees the uniqueness of its implementors. Here's an example implementation:
public final class Array implements Functor, T> {
private final T[] _values;
@SafeVarargs
public Array(T... values) {
_values = values;
}
@SuppressWarnings("unchecked")
@Override
public , A>> RESULT fmap(Function f) {
A[] result = (A[]) new Object[_values.length];
for (int i = 0; i < _values.length; ++i) {
result[i] = f.apply(_values[i]);
}
return (RESULT) new Array(result);
}
}
The cast to (RESULT) is safe because there can only be one type that matches "Functor, T>", and that's "Array". The disadvantage of this, is that generic code may need to pass around this "CONCRETE" type in a bunch of places, and it makes your signatures unwieldy. For instance:
public class Test {
public static , FBool extends Functor> FBool intToBool(FInt ints) {
return ints.fmap(x -> x > 5);
}
public static void main() {
Array ints = new Array<>();
Array bools1 = ints.fmap(x -> x > 5); // this works because Array<> implements fmap covariantly
Array bools2 = intToBool(ints); // but this also works thanks to our dummy CONCRETE type
}
}