简体   繁体   中英

Java Generics Inferred Types

Ok, so I am implementing the state monad in java. However, I can't seem to get the generics to work right. I have the code below, and am trying to avoid the cases indicated.

public interface Monad<M, A> 
{
    <B, R extends Monad<M, B>> R bind(Function<? super A, R> p_function);
}

public class State<S, A> implements Monad<State<S, ?>, A>
{
    private Function<S, Pair<S, A>> m_function;

    public State(Function<S, Pair<S, A>> p_function)
    {
        m_function = p_function;
    }

    public final Pair<S, A> run(S p_state)
    {
        return m_function.apply(p_state);
    }

    @Override
    public <B, R extends Monad<State<S, ?>, B>> R bind(
            final Function<? super A, R> p_function) 
    {
        // I want to avoid the cast to R here
        return (R) new State<S, B>((S state) -> {
            Pair<S, A> run = run(state);
            // And this cast, but they seem related
            State<S, B> applied = (State<S, B>) p_function.apply(run.second());
            return applied.run(run.first());
        });
    }
}


Note: I am aware that if I cange the signature of bind to
<B> Monad<M, B> bind(Function<? super A, ? extends Monad<M, B>> p_function);
The cast can be avoided. However, this causes a compile error in the following method

public static <A, B, C, M, MB extends Monad<M, B>, MC extends Monad<M, C>> 
Function<A, MC> compose(
        Function<? super A, MB> p_first, Function<? super B, MC> p_second)
{
    // have to use an anonymous class here, because using a closure causes a
    // runtime error with the beta version of JDK 8
    return new Function<A, MC>() {

        @Override
        public MC apply(A arg) {
            MB monadOfB = p_first.apply(arg);
            return monadOfB.<C> bind(p_second); // <-- type error here
        }
    };
}


Now, I also tried changing the signature of compose in a similar manner. ie rather than MB extends Monad<M, B> I used Monad<M, B> where MB was used and similarly for MC. This makes the compose method compile. However, then the return type could not be correctly inferred by the callers of compose ie

Function<String, State<Integer, String>> left = ...; 
Function<String, State<Integer, String>> right = ...; 
Function<String, State<Integer, String>> composed = Monad.compose(left, right);

Doesn't work without specifying the types on the method call, whereas before it did.

How do I make all these generics play nicely together?

For your example to work, you need your classes to be defined similarly to:

class State<S, B> extends Monad<State<S, ?>, B> {}
class Monad<T, U> {}

R is a subclass of Monad<State<S, ?>, B> , and sb is a subclass of Monad<State<S, ?>, B> too, but there is no reason that it also is a R .

It is like writing:

Number n = 123.5d;
Integer i = n; //does not compile: cast required
Integer j = (Integer) n; //throws an exception

EDIT

I'm not familiar with what you are trying to achieve, and this simplication might not achieve your aim, but it would compile (I have removed the lambdas as I don't have a jdk8 compiler installed at the moment):

public class Test1 {

    public static <A, B, C, M> Function<A, Monad<M, C>> compose(final Function<? super A, Monad<M, B>> p_first, 
                                                                final Function<? super B, Monad<M, C>> p_second) {
        // have to use an anonymous class here, because using a closure causes a runtime error
        // with the beta version of JDK 8
        return new Function<A, Monad<M, C>>() {
            @Override
            public Monad<M, C> apply(A arg) {
                Monad<M, B> monadOfB = p_first.apply(arg);
                return monadOfB.bind(p_second); // <-- type error here
            }
        };
    }
}

interface Monad<M, A> {

    <B> Monad<M, B> bind(Function<? super A, Monad<M, B>> p_function);
}

class State<S, A> implements Monad<State<S, ?>, A> {

    private Function<S, Pair<S, A>> m_function;

    public State(Function<S, Pair<S, A>> p_function) {
        m_function = p_function;
    }

    public final Pair<S, A> run(S p_state) {
        return m_function.apply(p_state);
    }

    @Override
    public <B> Monad<State<S, ?>, B> bind(final Function<? super A, Monad<State<S, ?>, B>> p_function) {
        // I want to avoid the cast to R here
        return new State<S, B>(new Function<S, Pair<S, B>>() {
            public Pair<S, B> apply(S state) {
                Pair<S, A> run = run(state);
                // And this cast, but they seem related
                State<S, B> applied = (State<S, B>) p_function.apply(run.second());
                return applied.run(run.first());
            }
        });
    }
}

Here's a version that passes the Java 7 type checker:

class State<S, A> implements Monad<State<S, ?>, A> {
    private Function<S, Pair<S, A>> m_function;

    public State(Function<S, Pair<S, A>> p_function)
    {
        m_function = p_function;
    }

    public final Pair<S, A> run(S p_state)
    {
        return m_function.apply(p_state);
    }

    @Override
    public <B> Monad<State<S, ?>, B> bind(
            final Function<? super A, ? extends Monad<State<S, ?>, B>> p_function) {
        return new State<S, B>(
                new Function<S, Pair<S,B>>() {
                    public Pair<S,B> apply(S state) {
                        Pair<S, A> run = run(state);
                        // And this cast, but they seem related
                        State<S, B> applied = (State<S, B>) p_function.apply(run.second());
                        return applied.run(run.first());
                    }
        });
    }

    public static <A, B, C, M> 
    Function<A, Monad<M,C>> compose(
            final Function<? super A, ? extends Monad<M,B>> p_first, 
            final Function<? super B, ? extends Monad<M,C>> p_second) {
        return new Function<A, Monad<M,C>>() {
            @Override
            public Monad<M,C> apply(A arg) {
                Monad<M,B> monadOfB = p_first.apply(arg);
                return monadOfB.<C>bind(p_second);
            }
        };
    }
}

The problem with your original code was that you wanted to pick a particular instantiation for R but your signature allowed the caller to pick it. The signature <B, R extends Monad<State<S, ?>, B>> R bind(final Function<? super A, R> p_function) lets the caller choose an arbitrary subtype of Monad<State<S, ?>,B> and demand that bind return it. Your bind implementation didn't do that, though: it decided to always return a particular subtype of R of your choosing. So your signature was promising one thing, but your implementation was doing another.

My fix removes that promise. Instead of allowing the caller to choose a particular Monad<State<S,?>,B> subtype, the revised signature just promises to return some subtype -- which in this case means I can dispense with the generic variable entirely and just substitute the upper bound type directly in the signature.

A similar problem affected the compose method -- allowing the caller to choose MB and MC types, but then expecting to be able to produce your own choice of subtypes for MB and MC in the implementation. I fixed that in a similar way.

(I'm +1'ing jacobm's answer, I just wanted to elaborate a bit on the underlying problem.)

The problem is that in Java, there is no particular relationship between GenericClass<S> and GenericClass<T> : I mean, both are subtypes of GenericType<?> , but there's no way that GenericInterface<T> can refer to the type you'd get by taking getClass() and substituting T for S .

In Haskell, the definition of the Monad typeclass looks like this:

class Monad m where
    (>>=)  :: m a -> (a -> m b) -> m b
    return :: a -> m a

Note that it defines m by using both ma and mb , meaning "the same parameterized type m , with (potentially) different type-arguments a and b ". In Java you can't create a supertype of m (that is, an interface for m ) that expresses this sort of thing, because while the supertype can refer to itself with arbitrary type-parameters (because it can refer to itself by name, just like how it could use any other generic type), and can refer to an arbitrary subtype with any single type-argument (namely its own), it has no way to refer to an arbitrary subtype with an arbitrary type-parameter. It doesn't sit "outside" the type system in the way that a Haskell typeclass definition does.

This means that there's no real way to define a generic Monad interface whose implementations are generic monadic types.

Tell me if this simplification is still representative of your question:

The following compiles:

public class TestClass {
    public interface Monad {
        <R extends Monad> R bind();
    }

    public class State implements Monad {
        @Override
        public <R extends Monad> R bind() {
            return (R) new State(); // [1]
        }
    }

    public <M extends Monad> M apply() {
        M subMonad = null;
        return subMonad.bind();
    }
}

You want to remove the cast from [1]. If you do it this way:

public class TestClass {
    public interface Monad {
        Monad bind();
    }

    public class State implements Monad {
        @Override
        public Monad bind() {
            return new State();
        }
    }

    public <M extends Monad> M apply() {
        M subMonad = null;
        return subMonad.bind(); // [2]
    }
}

Then [2] doesn't compile. Does this sum it up? If so, can the apply() method simply return a Monad ?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM