Re:Smashing Swift
by twic on Monday June 16, 2014 @06:57AM
(#7898294)
You can't do it directly, but there is a standard idiom for doing something like that - define the interface like this:
interface Functor<A, R extends Functor<A, R>>
And then in the implementing class, bind R to the implementing class:
class Option<A> implements Functor<A, Option<A>>
However, this doesn't let you write an actual functor. Try to write the map method on it:
class Option<A> implements Functor<A, Option<A>> {
private final A value;
public Option(A value) { this.value = value; }
@Override
public <B> Option<A> map(Function<A, B> fn) {
// er, i want to return an Option<B>, not an Option<A>
}
}
The problem is that you don't want to return an instance of the type of the receiver, you want to return an instance of a similar type with a different type parameter (Option<B> rather than Option<A>).
You would be absolutely fine if your functions were functions from values of some type to other values of that type (eg functions from integers to integers), because then A = B and you can get away with this. But there's no way to extend this hack to let you return an Option<B> from map.
The closest you can get in Java is probably this:
interface Functor<A, R extends Functor<?, R>>
class Option<A> implements Functor<A, Option<?>> {
private final A value;
public Option(A value) { this.value = value; }
@Override
public <B> Option<?> map(Function<A, B> fn) {
return new Option<B>(value != null ? fn.apply(value) : null);
}
}
There, you leave the element type of the returned functor undefined, as a wildcard, which lets you slip an Option<B> out as a return value.
The problem with that is that it's useless. Given:
Option<String> x;
Function<String, Integer> fn;
Then mapping the function over the option gives you:
Option<?> y = x.map(fn);
An Option<?>. Which is no use to man or beast, because it's lost the type information.
What you really want to be able to write is:
class Option<A> implements Functor<A, Option<_>>
Where the underscore is borrowed from Scala to mean "nothing to see here, move along please", and leaves that reference to Option in its unbound form. And then have a way of binding it right in the definition of the map function. But that's higher-kinded types, and Java doesn't have that.
If you were absolutely desperate to do this in Java, like if terrorists burst in and put a gun to your head and told you to do it, you could restore the type information by pebble-dashing the functor with some simple reflection:
interface Functor<A, R extends Functor<?, R>> {
<B> R map(Function<A, B> fn);
<B> Functor<B, R> as(Class<B> b);
}
class Option<A> implements Functor<A, Option<?>> {
// other stuff as above
@Override
public <B> Option<B> as(Class<B> b) {
return new Option<B>(b.cast(value));
}
}
Which, given the above definitions of x and fn, lets you write:
Option<Integer> y = x.map(fn).as(Integer.class);
And even the same thing but typed as functors, to show you there's nothing up my sleeve:
Functor<String, Option<?>> fx = x;
Functor<Integer, Option<?>> fy = fx.map(fn).as(Integer.class);
And as a separate function which contains no mention of the concrete functor class anywhere (although it does still need a type token for the result functor's parameter):
But to be honest, all of this is a bit like trying to carve a fine wooden sculpture with a chainsaw. If you want to do this, don't use Java. If you want to use Java, don't do this.
You would be absolutely fine if your functions were functions from values of some type to other values of that type (eg functions from integers to integers), because then A = B and you can get away with this. But there's no way to extend this hack to let you return an Option<B> from map.
The closest you can get in Java is probably this:
There, you leave the element type of the returned functor undefined, as a wildcard, which lets you slip an Option<B> out as a return value.The problem with that is that it's useless. Given:
Then mapping the function over the option gives you: An Option<?>. Which is no use to man or beast, because it's lost the type information.What you really want to be able to write is:
Where the underscore is borrowed from Scala to mean "nothing to see here, move along please", and leaves that reference to Option in its unbound form. And then have a way of binding it right in the definition of the map function. But that's higher-kinded types, and Java doesn't have that.If you were absolutely desperate to do this in Java, like if terrorists burst in and put a gun to your head and told you to do it, you could restore the type information by pebble-dashing the functor with some simple reflection:
Which, given the above definitions of x and fn, lets you write: And even the same thing but typed as functors, to show you there's nothing up my sleeve: And as a separate function which contains no mention of the concrete functor class anywhere (although it does still need a type token for the result functor's parameter): But to be honest, all of this is a bit like trying to carve a fine wooden sculpture with a chainsaw. If you want to do this, don't use Java. If you want to use Java, don't do this.