是否可以在Java中实现"Functor<T>"?



我今天偶然发现了一个小问题。考虑一个小包装类:

class Event<T> {
   T value;
   Class<T> type;
   // other fields, getters and setters omitted for brevity
}

现在,我想将Event<Long>转换为Event<String>,同时保留其他字段并更新type成员。

最终我得到了最简单的"解决方案":

Event<String> new = new Event(old.getValue().toString(), String.class, other, fields);

然而,在与Haskell合作过我的宠物项目之后,我自然而然地渴望像fmap :: Functor f => (a -> b) -> f a -> f b这样的函数(阅读:给定一个从a到b的函数和一个包含a类型的函子,给我一个包含b的结果),在发现没有标准实现后,我开始自己编写一个:

interface Functor<T> {
  Functor<S> fmap( Func1<T,S> f );
}
// ... in Event<T>:
Functor<S> fmap( Func1<T,S> f ) { 
  S newValue = f.call(this.value);
  return new Event( newValue, newValue.getClass(), other, fields);
}

现在这个解决方案存在一个问题:在 Java 中调用 fmap 后,我留下了一个类型 Functor<String> 的实例,而 Haskell 中的相同函数将返回一个 Event<String>

有没有办法让我的Event回来(不安全地投射它)?

不,这是不可能的。为此,我们需要在界面中的Functor上抽象,例如

interface Functor<T> as F<T> {
    F<S> map(f : Function<T, S>);
}

但是Java不允许你抽象类型构造函数,只允许类型。这被称为高等类型(HKT)。只有少数(非依赖)语言有HKT,Scala和Haskell是我能想到的仅有的两种。

事实上,HKTs对于表达很多抽象是必要的,

  1. 控制莫纳德
  2. 控制.应用
  3. 数据可遍历
  4. 数据.可折叠
  5. Control.Monad.Trans
  6. 任何单变压器,全挡
  7. 免费单子
  8. 透镜/棱镜
  9. 流库(它们是单元转换器)
  10. 几乎所有的东西都在category-extras

所有这些都涉及对类型构造函数的抽象,而不仅仅是具体类型。

对我来说效果很好,但还不是很Functor<T>. 它还要求您指定 F ,您的函子是其实例的类型:

interface Fn1<A, B> {
  B apply(A a);
}
interface Functor<A, F extends Functor<?, ?>> {
  <B> F map(Fn1<A, B> f);
}

您的Event<A>类将实现如下Functor

public class Event<A> implements Functor<A, Event<?>> {
  public final A value;
  public Event(A _value) {
    value = _value;
  }
  public <B> Event<B> map(Fn1<A, B> f) {
    return new Event<B>(f.apply(value));
  }
  public String toString() {
    return "Event<" + value.getClass().getSimpleName() + ">(" + value.toString() + ")";
  }
}

如果您需要闭合到扩展类的函子(例如 Observable来自 rxjava),你可以编写一个看起来更像类型类的东西,但是它不能符合任何类似函子的接口,因为 Java 缺乏更高级的类型:

public class ObservableFunctor {
  public static <A,B> Observable<B> map(Observable<A> fa, Fn1<A, B> f) {
    return new Observable<B>(f.apply(fa.value));
  }
}

下面是一个可运行的示例,它同时使用上面的EventObservable

public class FunctorDemo {
  interface Fn1<A, B> {
    B apply(A a);
  }
  interface Functor<A, F extends Functor<?, ?>> {
    <B> F map(Fn1<A, B> f);
  }
  static class ObservableFunctor {
    public static <A,B> Observable<B> map(Observable<A> fa, Fn1<A, B> f) {
      return new Observable<B>(f.apply(fa.value));
    }
  }
  static class Observable<A>  {
    public final A value;
    public Observable(A _value) {
      value = _value;
    }
    public String toString() {
      return "Observable<" + value.getClass().getSimpleName() + ">(" + value.toString() + ")";
    }
  }
  static class Event<A> implements Functor<A, Event<?>> {
    public final A value;
    public Event(A _value) {
      value = _value;
    }
    public <B> Event<B> map(Fn1<A, B> f) {
      return new Event<B>(f.apply(value));
    }
    public String toString() {
      return "Event<" + value.getClass().getSimpleName() + ">(" + value.toString() + ")";
    }
  }
  public static void main(String[] args) {
    Observable<Event<Long>> oe1 = new Observable(new Event(42L));
    System.out.println("oe1: " + oe1.toString()); // oe1: Observable<Event>(Event<Long>(42))
    Observable<Event<String>> oe2 = ObservableFunctor.map(oe1,
      new Fn1<Event<Long>, Event<String>>() {
        public Event<String> apply(Event<Long> e) {
          return e.map(
            new Fn1<Long, String>() {
              public String apply(Long l) {
                return l.toString();
              }
            }
          );
        }
      }
    );
    System.out.println("oe2: " + oe2.toString()); // oe2: Observable<Event>(Event<String>(42))
  }
}

最新更新