【问题标题】:Is it possible to implement `Functor<T>` in Java?是否可以在 Java 中实现 `Functor<T>`?
【发布时间】:2014-04-11 21:31:20
【问题描述】:

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

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

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

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

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

然而,在我的宠物项目中与 Haskell 合作后,我自然渴望像 fmap :: Functor f =&gt; (a -&gt; b) -&gt; f a -&gt; 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&lt;String&gt; 类型的实例,而 Haskell 中的相同函数将返回一个 Event&lt;String&gt;

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

【问题讨论】:

  • 这里需要更高种类的多态性。您需要对 S 和 T 变量以及称为 Functor 的部分进行抽象。
  • Java 的类型系统不能真正代表更高种类的类型构造函数。
  • Event类中fmap的返回类型改为Event&lt;S&gt;。由于EventFunctor 的子类,这应该可以工作。

标签: java haskell functor


【解决方案1】:

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

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

但Java 不允许您对类型构造函数进行抽象,而只能对类型进行抽象。这称为高级类型(HKT)。只有少数(非依赖)语言有 HKT,Scala 和 Haskell 是我能想到的仅有的两个。

其实HKTs是表达很多抽象所必需的,

  1. Control.Monad
  2. Control.Applicative
  3. Data.Traversable
  4. Data.Foldable
  5. Control.Monad.Trans
  6. 任何单子变压器,句号
  7. 免费单子
  8. 镜头/棱镜
  9. 流媒体库(它们是 monad 转换器)
  10. category-extras 中的几乎所有内容

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

【讨论】:

  • 这在 c# 中是可能的,请参阅以下答案的后半部分:stackoverflow.com/a/21366216/414413。我对 Java 泛型不够熟悉,无法说出 Java 中是否可以使用相同的技巧。
  • Java 允许实现/扩展类将方法的返回类型更改为父方法 (pastebin.com/4vzAVVTN) 的返回类型的子类,这样应该有助于解决 OP 的问题。
【解决方案2】:

这对我来说相当不错,但它并不完全是Functor&lt;T&gt;。它要求您还指定 F,您的仿函数是其实例的类型:

interface Fn1<A, B> {
  B apply(A a);
}

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

您的Event&lt;A&gt; 类将像这样实现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))

  }
}

【讨论】:

  • 但遗憾的是对我不起作用......不知道为什么。有一些嵌套的泛型 (Observable&lt;Event&lt;Float[]&gt;&gt;&gt;) ...它只是不想玩得很好。
  • 您还需要Observable 来成为/拥有函子吗?
  • 我更新了上面的例子来创建一个嵌套的Observable&lt;Event&lt;Long&gt;&gt; 类型。这是否更接近您的需求?
  • Naa ...问题是Observable来自rxjava并且对我关闭,所以我不能以任何方式扩展它。
  • 啊,这很棘手。我更新了上述内容,使Observable 不是函子,而是使用类似函子的ObservableFunctor
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-04
  • 1970-01-01
  • 2013-01-22
  • 1970-01-01
  • 1970-01-01
  • 2014-07-09
  • 2015-03-28
相关资源
最近更新 更多