6

好的,所以我在 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());
        });
    }
}


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

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<M, B>我在使用Monad<M, B>MB 的地方使用,对于 MC 也是如此。这使得compose方法编译。compose但是, ie的调用者无法正确推断出返回类型

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

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

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

4

4 回答 4

3

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

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

R是 的子类Monad<State<S, ?>, B>,而 sb 也是 的子类Monad<State<S, ?>, B>,但没有理由说它也是 a 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());
            }
        });
    }
}
于 2013-07-16T09:01:31.430 回答
2

这是通过 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但您的签名允许调用者选择它。签名<B, R extends Monad<State<S, ?>, B>> R bind(final Function<? super A, R> p_function)允许调用者选择任意子类型Monad<State<S, ?>,B>并要求bind返回它。但是,您的bind实现并没有这样做:它决定始终返回R您选择的特定子类型。因此,您的签名承诺了一件事,但您的实施却在做另一件事。

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

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

于 2013-07-19T03:18:24.703 回答
2

(我 +1'ing jacobm's answer,我只是想详细说明一下潜在问题。)

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

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

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

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

这意味着没有真正的方法来定义Monad其实现是泛型一元类型的泛型接口。

于 2013-07-21T03:41:15.530 回答
0

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

以下编译:

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吗?

于 2013-07-19T01:18:55.087 回答