Javagenerics:如何用Java编码Functor接口?

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

//a Function public interface F { public R apply(A a); } public interface Functor { public  Functor fmap(F f); } 

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

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

[澄清]

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

 public class ListFunctor implements ??? { final private List list; public ListFunctor(List list) { this.list = list; } @Override  ListFunctor fmap(F f) { List result = new ArrayList(); for(A a: list) result.add(f.apply(a)); return new ListFunctor(result); } } 

我知道即使使用我给出的第一个定义也可以写这个(因为允许使用协变返回类型),但是我希望返回类型“ListFunctor”由类型系统强制执行 (这样我就不能返回相反,FooFunctor),这意味着Functor接口需要返回“自我类型”(至少它在其他语言中被称为)。

[结果]

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

[后果]

我偶然发现了这个古老的问题,并意识到这是我的图书馆highJ的惊人旅程的起点,它不仅仅包含一个简单的Functor 。 我永远不会想象人们会把这些疯狂的东西用于任何严肃的事情,但事情发生了,这让我很开心。

 public interface Functor> { public > I fmap(F f); } 

此代码生成错误,因为当您定义I ,将其定义为Functor的子类,但在这种情况下,FInst参数必须是Functor的子类,而它在上面定义作为Functor的子类。 由于FunctorFunctor不兼容,因此会出现此错误。

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

 import java.util.ArrayList; import java.util.List; interface F { public R apply(A a); } interface Functor> { public  FClass fmap(F f); } public class ListFunctor implements Functor> { final private List list; public ListFunctor(List list) { this.list = list; } @Override public  ListFunctor fmap(F f) { List result = new ArrayList(); for(A a: list) result.add(f.apply(a)); return new ListFunctor(result); } } 

这是有效的,并且它正确地将允许的返回类型集限制为ListFunctor,但它不仅限于ListFunctor子类。 您可以将其声明为返回ListFunctor或任何其他ListFunctor,它仍然可以编译。 但是您不能将其声明为返回FooFunctor或任何其他Functor。

解决其余问题的主要问题是你不能只将FClass限制为ListFunctor子类,因为B参数是在方法级别而不是在类级别声明的,所以你不能写

 public class ListFunctor implements Functor> { 

因为B在那一点上没有任何意义。 我无法使用fmap()的第二个参数,但即使我可以,它只会强制您指定两次返回类型 – 一次在type参数中,再一次作为返回类型本身。

从不同的角度来看,似乎Functor不应该被建模为围绕数据的“包装器”,而实际上更像是一个类型类,它可以处理数据。 这种透视转换允许在没有单个转换的情况下编码所有内容,并且绝对类型安全(但仍然有很多样板):

 public interface Functor { public ToInstance fmap(FromInstance instance, F f); } public class ListFunctor implements Functor, List> { @Override public List fmap(List instance, F f) { List result = new ArrayList(); for(A a: instance) result.add(f.apply(a)); return result; } } List stringList = Arrays.asList("one","two","three"); ListFunctor functor = new ListFunctor(); List intList = functor.fmap(stringList, stringLengthF); System.out.println(intList); //--> [3, 3, 5] 

我似乎过于专注于在一个类型参数(例如ListFunctor中的List)中打包FromInstance和ToInstance,这不是绝对必要的。 然而,现在不仅A而且B作为类型参数是一个沉重的负担,这可能使这种方法几乎无法使用。

[研究]

我发现了一种使这个版本至少有用的方法:这个仿函数可以用来解除一个函数。 例如,如果你有F ,当你有如上所示定义的FooFunctor时,可以从它构造一个F, Foo>

 public interface F { public B apply(A a); public  F lift( Functor functor); } public abstract class AbstractF implements F { @Override public abstract B apply(A a); @Override public  F lift( final Functor functor) { return new AbstractF() { @Override public ToInstance apply(FromInstance fromInstance) { return functor.fmap(fromInstance, AbstractF.this); } }; } } public interface Functor { public ToInstance fmap(FromInstance instance, F f); } public class ListFunctor implements Functor, List> { @Override public List fmap(List instance, F f) { List result = new ArrayList(); for (A a : instance) { result.add(f.apply(a)); } return result; } } //Usage: F strLenF = new AbstractF() { public Integer apply(String a) { return a.length(); } }; //Whoa, magick!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! F,List> liftedF = strLenF.lift(new ListFunctor()); List stringList = Arrays.asList("one", "two", "three"); List intList = liftedF.apply(stringList); System.out.println(intList); //--> [3, 3, 5] 

我认为它仍然不是很有用,但至少比其他尝试更酷:-P

在谢尔盖的回答基础上,我想我接近了我想要的东西。 似乎我可以将他的想法与我失败的尝试结合起来:

 public interface Functor> { public > I fmap(F f); } public class ListFunctor implements Functor> { final private List list; public ListFunctor(List list) { this.list = list; } @Override public >> I fmap(F f) { List result = new ArrayList(); for(A a: list) result.add(f.apply(a)); return (I) new ListFunctor(result); } } List list = java.util.Arrays.asList("one","two","three"); ListFunctor fs = new ListFunctor(list); ListFunctor fi = fs.>fmap(stringLengthF); //--> [3,3,5] 

剩下的问题是我可以编写例如ListFunctor fi = fs.>而无需编译器投诉。 至少我可以寻找一种方法来隐藏静态方法背后的丑陋内容,并在幕后加强这种关系……

我想你想做一些没有意义的事情(明智的)。

 interface Getter { Type get(); } 

如果您的应用程序想要一个返回Integers的getter,请不要给它一个返回Objects的。

如果你不知道它是否会返回对象或整数,你试图以错误的方式做某事。

如果你知道它将返回Integers,那么将getter包装起来以便它转换为整数。

希望这是你正在寻找的。

编辑:解释为什么(我认为)这不能做到。

使用new时,对象的类型设置为。 取每种类型并用字母替换它。 获取任意数量的其他对象并执行相同操作。

你希望你的函数返回什么字母? 如果答案是你想要混合,那么为时已晚。 类型是新的,你已经过了新的。