Derived Java Generics Types

Ok, so I implement the state monad in java. However, I cannot get generics to work correctly. I have the code below and I try to avoid these cases.

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 know that if I can add the bind signature to <B> Monad<M, B> bind(Function<? super A, ? extends Monad<M, B>> p_function);
Cast can be avoided. However, this causes a compilation 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 compose signature in a similar way. that is, instead of MB extends Monad<M, B> I used Monad<M, B> , where MB was used and similarly for MC. This allows you to compile the compose method. However, the return type cannot be correctly deduced by callers compose ie

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

It does not work without specifying types in a method call, whereas earlier it was done.

How do I make all of these generics work well together?

+6
source share
4 answers

(I am responding to jacobm, I just wanted to sort out the main problem a bit.)

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

In Haskell, the Monad class definition is as follows:

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

Note that it defines m using both ma and mb , which means "the same parameterized type m , with (potentially) different arguments of type a and b ". In Java, you cannot create a supertype m (i.e. an interface for m ) that expresses such a thing, because although a supertype can refer to itself with arbitrary type parameters (since it can refer to itself by name, in the same way how it can use any other common type), and can refer to an arbitrary subtype with any one type argument (namely, its own), it has no way to refer to an arbitrary subtype with an arbitrary type - parameter. It does not sit "outside" the type system in the same way as the Haskell class definition does.

This means that there is no real way to define a common Monad interface whose implementation is a common monadic type.

+2
source

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

 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 also a subclass of Monad<State<S, ?>, B> , but there is no reason that it should also be R

This is similar to the entry:

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

EDIT

I am not familiar with what you are trying to achieve, and this simplification may not reach your goal, but it will be compiled (I removed the lambdas, because at the moment I do not have the jdk8 compiler):

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

Here is the version that passes the Java 7 type check:

 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 source code was that you wanted to select a specific instance for R , but your signature allowed the caller to select it. The signature <B, R extends Monad<State<S, ?>, B>> R bind(final Function<? super A, R> p_function) allows the caller to select an arbitrary subtype of Monad<State<S, ?>,B> and require bind return it. However, the bind implementation did not do this: he decided to always return a specific subtype of R of your choice. So, your signature promised one thing, but your implementation did another.

My fix removes this promise. Instead of letting the caller select a specific subtype of Monad<State<S,?>,B> , the revised signature is simply promises to return a certain subtype, which in this case means that I can completely abandon the general variable and simply substitute the top type of binding directly to the signature.

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

+2
source

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

The following compilations:

 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 throw from [1]. If you do like this:

 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] does not compile. Does this mean it? If so, can the apply() method return a Monad ?

0
source

Source: https://habr.com/ru/post/949538/


All Articles