Java泛型推断类型

ekj*_*ekj 6 java generics

好的,所以我在java中实现状态monad.但是,我似乎无法让泛型工作正常.我有下面的代码,我正在努力避免指出的情况.

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());
        });
    }
}
Run Code Online (Sandbox Code Playgroud)


注:我知道,如果我cange的签名bind
<B> Monad<M, B> bind(Function<? super A, ? extends Monad<M, B>> p_function);
可避免的演员.但是,这会导致以下方法中的编译错误

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
        }
    };
}
Run Code Online (Sandbox Code Playgroud)


现在,我也尝试compose以类似的方式更改签名.也就是说,而不是MB extends Monad<M, B>我使用Monad<M, B>MB的地方,而不是我使用的MC.这使得该compose方法编译.然而,composeie 的调用者无法正确推断返回类型

Function<String, State<Integer, String>> left = ...; 
Function<String, State<Integer, String>> right = ...; 
Function<String, State<Integer, String>> composed = Monad.compose(left, right);
Run Code Online (Sandbox Code Playgroud)

如果没有在方法调用上指定类型,则不起作用,而在此之前.

如何使所有这些仿制药一起很好地发挥作用?

rua*_*akh 2

(我+1'ing jacobm的答案,我只是想详细说明一下根本问题。)

GenericClass<S>问题是,在 Java 中,和之间没有特定的关系GenericClass<T>:我的意思是,两者都是 的子类型GenericType<?>,但是没有办法可以引用通过采用并替换来GenericInterface<T>获得的类型。getClass()TS

在 Haskell 中,类型类的定义Monad如下所示:

class Monad m where
    (>>=)  :: m a -> (a -> m b) -> m b
    return :: a -> m a
Run Code Online (Sandbox Code Playgroud)

请注意,它m通过同时使用m a和 来定义m b,意思是“相同的参数化类型m,具有(可能)不同的类型参数ab”。在 Java 中,您无法创建表达此类事物的超类型m(即 的接口),因为虽然超类型可以使用任意类型参数引用自身(因为它可以通过名称引用自身,就像它如何使用任何其他泛型类型),并且可以使用任何单个类型参数(即它自己的)引用任意子类型,它无法使用任意类型参数引用任意子类型。它不像 Haskell 类型类定义那样位于类型系统“外部”。m

这意味着没有真正的方法来定义Monad其实现是通用单子类型的通用接口。