News for hackers. Stuff that matters.
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):

    private <A, B, F extends Functor<?, F>> Functor<B, F> applyAbstractly(Functor<A, F> fx, Function<A, B> fn, Class<B> b) {
        return fx.map(fn).as(b);
    }
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.

There are no comments on this comment yet.