6

わかりましたので、状態モナドを Java で実装しています。ただし、ジェネリックを正しく機能させることができないようです。以下のコードがあり、示されているケースを回避しようとしています。

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注: to の署名を変更すると
<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
        }
    };
}


composeさて、同様の方法で の署名も変更してみました。MB extends Monad<M, B>つまり、MB が使用された場所で使用したのではなく、Monad<M, B>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);

メソッド呼び出しで型を指定しないと動作しませんが、以前は動作していました。

これらすべてのジェネリックをうまく連携させるにはどうすればよいですか?

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>が、それも である理由はありませんR

それは書くようなものです:

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

編集

私はあなたが達成しようとしていることに精通していません.この単純化はあなたの目的を達成しないかもしれませんが、コンパイルされます(現時点では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

(私はjacobmの答えを+1しています。根本的な問題について少し詳しく説明したかっただけです。)

問題は、Java では、 と の間GenericClass<S>に特定の関係がないことです。つまりGenericClass<T>、どちらも のサブタイプですが、 を取得して置換することで得られる型を参照するGenericType<?>方法はありません。GenericInterface<T>getClass()TS

Haskell では、型クラスの定義はMonad次のようになります。

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

mと の両方を使用m aして定義されていることに注意してください。つまり、「 (潜在的に) 異なる型引数を持つm b同じパラメーター化された型 と 」を意味します。Java では、このようなことを表現する のスーパータイプ(つまり、 のインターフェース) を作成することはできません。なぜなら、スーパータイプは任意の型パラメーターで自分自身を参照できるからです (名前で自分自身を参照できるため)他のジェネリック型をどのように使用できるか)、単一の型引数 (つまり、それ自体) を持つ任意のサブタイプを参照できますが、任意の型パラメーターを持つ任意のサブタイプを参照する方法はありません。Haskell 型クラス定義のように、型システムの「外側」にはありません。mabmm

Monadこれは、実装がジェネリック モナディック型であるジェネリック インターフェイスを定義する実際の方法がないことを意味します。

于 2013-07-21T03:41:15.530 に答える
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。選択した特定のサブタイプを常に返すことにしました。したがって、署名は 1 つのことを約束していましたが、実装は別のことを行っていました。

私の修正はその約束を取り除きます。呼び出し元が特定のサブタイプを選択できるようにする代わりに、改訂されたシグネチャは、何らかのMonad<State<S,?>,B>サブタイプを返すことを約束するだけです。つまり、この場合、ジェネリック変数を完全に省略して、上限の型をシグネチャで直接置き換えることができます。

同様の問題がメソッドに影響を与えましcomposeた。呼び出し元がMBand型を選択できるようにしながら、実装でandMCのサブタイプを独自に選択できることを期待していました。同様の方法で修正しました。MBMC

于 2013-07-19T03:18:24.703 に答える
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 に答える