Проблемы с Java Generics при построении монады - PullRequest
3 голосов
/ 27 января 2020

Я играю вокруг Java кода, чтобы создать монаду функционального стиля, но я поражен, когда использую дженерики, и компилятор Java выдает мне ошибку компиляции, если я не приведу свой объект (хотя Generics решил бы эту проблему!)

Это использование:

//COMPILATION ERROR! It requires a cast to String
String message = If.of("Hi", s->s!=null).apply(s->s+" guys!").get();

Разрешено:

Это моя монада:

import java.util.function.Function;
import java.util.function.Predicate;

public class If<T, R> {

  private T t;
  private Predicate predicate;

  private If(T t, Predicate predicate) {
    this.t = t;
    this.predicate = predicate;
  }

  public static<T> If of(T t, Predicate predicate) {
    return new If(t, predicate);
  }

  public If<R,R> apply(Function<T, R> function) {
    if(predicate!=null && predicate.test(t)){
        return new If<R, R>(function.apply(t), null);
    }
    return If.of(this.t, null);
  }

  public T get() {
    return t;
  }

}

Ответы [ 2 ]

6 голосов
/ 27 января 2020

Непосредственная проблема заключается в том, что тип возвращаемого значения of является необработанным:

public static<T> If of(T t, Predicate predicate) {

Возможно, вам нужно что-то вроде:

public static<T> If<T, Something> of(T t, Predicate<T> predicate) {

Я бы сказал, что вы на самом деле не хотите испечь R типа If. Если вы вместо этого объявите его в методе, у вас будет возможность apply указать его для любого типа:

public class If<T> {
  // ...

  public <R> If<R> apply(Function<T, R> function) {
    if(predicate!=null && predicate.test(t)){
      return new If<>(function.apply(t), null);
    }
    return If.of(this.t, null);
  }

  // ...
}

Тогда ваша подпись of может быть просто:

public static<T> If<T> of(T t, Predicate<T> predicate) {

Если вы хотите, чтобы ваш API был более гибким, добавьте подстановочные знаки:

public static<T> If<T> of(T t, Predicate<? super T> predicate) {

и

public <R> If<R> apply(Function<? super T, ? extends R> function) {
3 голосов
/ 27 января 2020

Ответ Энди Тернера немедленно объяснил, почему ваш текущий код не компилируется, но ваша монада, кажется, имеет более фундаментальную проблему - она ​​не очень полезна.

По вашему мнению, первый вызов apply должен возвращать преобразованный объект, заключенный в монаду, если условие истинно, или исходный объект, заключенный в монаду, если условие ложно. Но поскольку вы передаете null в качестве условия для обоих случаев, любые последующие вызовы на apply приведут к достижению второго return, следовательно, всегда возвращают результат первого вызова на apply.

Фактически, невозможно ни вернуть исходный объект, ни преобразованный объект (во всяком случае, бесполезным и безопасным для типов способом). Чтобы сделать это безопасно для типа, вам понадобится Either<If<T>, If<R>> (при условии, что такой тип существует). Но чтобы извлечь значения из Either, вам все равно понадобится оператор if, который победит назначение класса If<T>.

По-видимому, это всего лишь упражнение для практики написания монад. В этом случае я рекомендую вам выбрать другую монаду для реализации, например Either. Я также предлагаю вам сначала посмотреть этот пост .

...