是否可以在Java中实现`Functor <t>`</t>

时间:2014-04-08 17:55:24

标签: java haskell functor

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

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

现在我处于这样一种情况:我希望将Event<Long>转换为Event<String>,同时保留其他字段并更新type成员。

最终我得到了最简单的解决方案&#34;:

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回来(没有不安全地投射它)?

2 个答案:

答案 0 :(得分:8)

不,这是不可能的。要实现这一点,我们需要在您的界面中对Functor进行抽象,例如

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

但Java不允许您通过类型构造函数进行抽象,只需类型。这被称为高级金属类型(HKT)。只有少数(非依赖)语言有HKT,Scala和Haskell是我能想到的唯一两种语言。

事实上HKT是表达大量抽象的必要条件,

  1. Control.Monad
  2. Control.Applicative
  3. Data.Traversable
  4. Data.Foldable
  5. Control.Monad.Trans
  6. 任何monad变形金刚,完全停止
  7. 免费monads
  8. 透镜/棱镜
  9. 流媒体库(它们是monad变换器)
  10. 几乎category-extras
  11. 中的所有内容

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

答案 1 :(得分:3)

这对我来说相当不错,但它并不完全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() + ")";
  }
}

如果你需要一个封闭式扩展类的仿函数(例如来自rxjava的Observable),你可以编写一个看起来更像类型类的仿函数,但它不能符合任何类似仿函数的接口,因为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))

  }
}