【问题标题】:Java Generics Inferred TypesJava 泛型推断类型
【发布时间】:2013-07-21 11:41:15
【问题描述】:

好的,所以我在 java 中实现了 state 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());
        });
    }
}


注意:我知道如果我将bind 的签名更改为
&lt;B&gt; Monad&lt;M, B&gt; bind(Function&lt;? super A, ? extends Monad&lt;M, B&gt;&gt; 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
        }
    };
}


现在,我也尝试以类似的方式更改compose 的签名。即,而不是MB extends Monad&lt;M, B&gt;,我使用Monad&lt;M, B&gt;,其中使用了MB,同样用于MC。这使得 compose 方法编译。但是,compose 的调用者无法正确推断返回类型,即

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

如果没有在方法调用上指定类型就无法工作,而在它之前。

如何让所有这些泛型很好地结合在一起?

【问题讨论】:

  • 我似乎无法理解第一个是如何工作的?
  • @f1sh:好吧,如果State extends Monad(随意添加泛型)它可能会起作用。
  • 可以添加StateMonad的定义吗?
  • OP 能否通过编造一些简单且可以证明您的问题的东西来简化您的问题?您的代码中有太多“噪音”,我认为没有人真正清楚地了解您的问题
  • @AdrianShum 我的简化尝试如下(在阅读了 OP 问题中的最新编辑后进行了更多编辑)。

标签: java generics


【解决方案1】:

为了使您的示例正常工作,您需要将类定义为:

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

RMonad&lt;State&lt;S, ?&gt;, B&gt; 的子类,sb 也是Monad&lt;State&lt;S, ?&gt;, B&gt; 的子类,但没有理由它也是R

这就像写作:

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

编辑

我不熟悉您要实现的目标,这种简化可能无法实现您的目标,但它会编译(我已经删除了 lambda,因为我目前没有安装 jdk8 编译器):

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());
            }
        });
    }
}

【讨论】:

  • 值得注意的是,OP 对该提案不满意,因为它删除了一层泛型。
【解决方案2】:

(我在为 jacobm 的回答 +1,我只是想详细说明一下潜在问题。)

问题在于,在 Java 中,GenericClass&lt;S&gt;GenericClass&lt;T&gt; 之间没有特别的关系:我的意思是,两者都是 GenericType&lt;?&gt; 的子类型,但 GenericInterface&lt;T&gt; 无法引用您的类型d 获取 getClass() 并将 T 替换为 S

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

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

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

这意味着没有真正的方法来定义一个通用的Monad 接口,它的实现是通用的一元类型。

【讨论】:

  • 这是一个有趣的观点,我知道这一点,但你的描述为我明确了这个概念。在我的实现中,我尝试通过让 monad 接口采用两个类型参数来解决这个问题; M monad 类型本身和 A 包装类型。
  • @ekj:是的,我看到了,但问题是你的M 类型参数需要以某种方式宣布“我是通用的!给我一个类型参数!(而M&lt;A&gt; 是特殊的!)”,而 Java 泛型根本不提供任何将类型参数标记为泛型的方法,也没有(因此)将类型参数 传递给 类型参数。您尝试通过使用通配符来解决此问题,例如Maybe&lt;A&gt; 将是 Monad&lt;Maybe&lt;?&gt;, A&gt;,但随后您遇到了该通配符中缺少编码的类型信息的问题。
  • 嗯...是的,不是通配符产生了问题。看来我正在达到 Java 类型系统的极限。根据我的阅读,我认为我可以实现单子而不需要更高阶的种类。不是这样吗?
  • @ekj: 你可以实现任何 specific monad,但是是的,你不能只使用静态类型系统来表达 monad 的 concept . (但 Java 也有一个“运行时类型”系统。通过一些聪明的做法,以及在各种类型上使用一些冗余的 Class&lt;?&gt; 字段来跟踪它们的类型参数,您可能可以安排一些事情,以便真正检查您的所有类型转换.)
【解决方案3】:

这是一个通过 Java 7 类型检查器的版本:

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);
            }
        };
    }
}

您的原始代码的问题是您想为R 选择一个特定的实例化,但您的签名允许调用者选择它。签名&lt;B, R extends Monad&lt;State&lt;S, ?&gt;, B&gt;&gt; R bind(final Function&lt;? super A, R&gt; p_function) 允许调用者选择Monad&lt;State&lt;S, ?&gt;,B&gt; 的任意子类型并要求bind 返回它。但是,您的 bind 实现并没有这样做:它决定始终返回您选择的 R 的特定子类型。所以你的签名承诺了一件事,但你的实现正在做另一件事。

我的修复取消了这个承诺。修改后的签名只是承诺返回 some 子类型,而不是允许调用者选择特定的 Monad&lt;State&lt;S,?&gt;,B&gt; 子类型——在这种情况下,这意味着我可以完全放弃泛型变量,而只需替换上直接在签名中绑定类型。

类似的问题影响了compose 方法——允许调用者选择MBMC 类型,但随后期望能够为MBMC 生成您自己选择的子类型实施。我以类似的方式解决了这个问题。

【讨论】:

  • 是的,我提到我已经尝试过这个解决方案。但是,这会导致在调用 compose 方法时必须指定类型参数。看我帖子最后的代码sn-p
  • 啊,错过了,抱歉。
【解决方案4】:

告诉我这种简化是否仍能代表您的问题:

以下编译:

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();
    }
}

您想从 [1] 中移除演员表。如果你这样做:

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]
    }
}

然后 [2] 无法编译。这能概括吗?如果是这样,apply() 方法是否可以简单地返回一个Monad

【讨论】:

  • 是的,我得到了那部分......但我真正想要的是一些关于如何让我的所有泛型一起发挥良好作用的指针
  • 那个解决方案(从应用返回 Monad)正是上面@jacobm 建议的,只是形式更简单。这会带来其他问题,请参阅我帖子中的最后一个代码 sn-p。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-01
  • 2015-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-28
相关资源
最近更新 更多