Some Haskell may help. Firstly, a functor:
class Functor f where
fmap :: (a -> b) -> f a -> f b
We can read this as saying if a type f
is a Functor
then there must have an fmap
function, which takes a function of type a -> b
and a value oif type f a
to yield an f b
. I.e. the type allows the function to be applied to the value within it.
Java doesn't have support for higher-kinded types, which would be required to define a functor type as above, so instead we have to approximate it:
interface Functor6<F, T> {
<U> Function<? extends Functor6<F, T>, ? extends Functor6<?, U>> fmap(Function<T, U> f);
}
Here the generic type parameter F
is the Functor type, equivalent to f
in the Haskell definition, and T
is the contained type (as is U
), equivalent to a
(and b
) in the Haskell definition. In the absence of HKTs we have to use wildcard to refer to the functor type (? extends Functor6<F, T>
).
Next, Applicative:
class (Functor f) => Applicative f where
pure :: a -> f a
<*> :: f (a -> b) -> f a -> f b
I.e., for a type f
to be an applicative it must be a functor, have a pure
operation which lifts a value into the applicative f
, and an apply operation (<*>
) which, given a function (a -> b
) inside an f
and an a
inside an f
, can apply the function to the value to yield a b
inside an f
.
Here's your Java equivalent, simplified using some Java 8 features, and with some types corrected:
interface Applicative<F, T> extends Functor6<F, T> {
T get();
<F, U> Applicative<F, U> unit(U value);
default <U> Function<Applicative<F, T>, Applicative<F, U>> apply(Applicative<?, Function<T, U>> ff) {
return ft -> {
Function<T, U> f = ff.get();
T t = ft.get();
return unit(f.apply(t));
};
}
}
If we take it line by line:
interface Applicative<F, T> extends Functor6<F, T> {
says an Applicative is a Functor. This:
T get();
appears to be a means of getting access to the value within the applicative. This may work for specific cases, but does not in general work. This:
<F, U> Applicative<F, U> unit(U value);
is supposed to be equivalent to the pure
function in the Haskell definition. It ought to be static, otherwise you need an applicative value in order to be able to call it, however making it static would prevent it from being overridden in the actual applicative implementation. There is no simple way to solve this in Java.
Then you have the apply
method, which by rights is supposed to be equivalent to <*>
in Haskell. As we can see it just gets the function f
out of the applicative and then its argument, and returns an applicative containing the result of applying the function to the argument.
This is where the wheels really come off. The details of how an applicative applies a function to a value is specific to each applicative and can't be generalised like this.
In short, this approach is wrong. That's not to say you can't implement applicative functors in Java - you can and it's quite easy, but what you can't do is state within the language that they are applicatives, as you can do in Haskell (using type classes).