一尘不染

Java泛型:如何用Java编码Functor接口?

java

我想用Java定义Functor类。这有效:

//a Function
public interface F<A,R> {
   public R apply(A a);
}

public interface Functor<A> {
   public <B> Functor<B> fmap(F<A,B> f);
}

但是,fmap的返回值应该不是Functor,而是适当的子类。通常,可以使用CRTP对此进行编码,但是由于附加参数,在这里我似乎遇到了麻烦A。例如,以下和类似的编码不起作用(“类型参数FInst不在其范围内”):

public interface Functor<A, FInst extends Functor<A,FInst>> {
    public <B, I extends Functor<B,FInst>> I fmap(F<A,B> f);
}

[说明]

对于“适当的子类”,我指的是被称为自身的类的类型。例如,列表是函子,所以我想写一些类似的东西

public class ListFunctor<A> implements ??? {
  final private List<A> list;
  public ListFunctor(List<A> list) {
     this.list = list;
  }

  @Override
  <B> ListFunctor<B> fmap(F<A,B> f) {
     List<B> result = new ArrayList<B>();
     for(A a: list) result.add(f.apply(a));
     return new ListFunctor<B>(result); 
  }  
}

我知道即使使用给出的第一个定义,我也可以编写此代码(因为允许使用协变量返回类型),但是我希望返回类型“ ListFunctor” 由类型系统
强制执行 (因此,我无法返回FooFunctor),这意味着Functor接口需要返回“自我类型”(至少在其他语言中如此)。

[结果]

所以看来我想要的是不可能的。这是一个相关的博客文章:http : //blog.tmorris.net/higher-order-
polymorphism-for-pseudo-java/

[后果]

我偶然发现了我这个古老的问题,意识到这是我的图书馆highJ令人惊叹的旅程的起点,该图书馆包含的内容远不止一个简单的Functor。我永远也不会想象人们会用这种疯狂的东西来处理任何严重的事情,但是事情发生了,这让我感到非常高兴。


阅读 290

收藏
2020-12-03

共1个答案

一尘不染

public interface Functor<A, FInst extends Functor<A,FInst>> {
    public <B, I extends Functor<B,FInst>> I fmap(F<A,B> f);
}

这段代码会产生错误,因为您在定义时I将其定义为的子类Functor<B,FInst>,但Functor<B,FInst>在这种情况下FInst参数必须是的子类,而在上面将其定义为的子类Functor<A,FInst>。由于Functor<A,FInst>Functor<B,FInst>不兼容,因此会出现此错误。

我无法完全解决此问题,但我至少可以完成一半的工作:

import java.util.ArrayList;
import java.util.List;

interface F<A,R> {
   public R apply(A a);
}

interface Functor<A, FClass extends Functor<?, FClass>> {
   public <B> FClass fmap(F<A,B> f);
}

public class ListFunctor<A> implements Functor<A, ListFunctor<?>> {
  final private List<A> list;
  public ListFunctor(List<A> list) {
     this.list = list;
  }

  @Override
  public <B> ListFunctor<B> fmap(F<A,B> f) {
     List<B> result = new ArrayList<B>();
     for(A a: list) result.add(f.apply(a));
     return new ListFunctor<B>(result);
  }
}

这行得通,并且将允许的返回类型的集合适当地限制为ListFunctor,但并不将其限制为ListFunctor<B>仅子类。您可以将其声明为returning
ListFunctor<A>或任何其他ListFunctor,并且仍然可以编译。但是您不能将其声明为返回FooFunctor或任何其他Functor。

解决其余问题的主要问题是,您不能将FClass限制为的子类ListFunctor<B>,因为B参数是在方法级别而不是在类级别声明的,因此您不能编写

public class ListFunctor<A> implements Functor<A, ListFunctor<B>> {

因为那时B并不意味着任何东西。我也无法使它与fmap()的第二个参数一起使用,但是即使可以,它也会强制您两次指定返回类型-
在type参数中指定一次,在返回类型本身中再指定一次。

2020-12-03