我正在尝试在Java中定义类似于Haskell函子的类。因此,仿函数定义为:
/**
* Programming languages allow only (just simply enough) endofunctor, that are functors from and to the same category.
* In this case, the category is the one of the datatypes (in here Type, in order to make it more clear)
*/
public interface EndoFunctor<X extends Type> extends Type {
/**
* The basic implementation for any element fx
* @param map Transformation function for the type parameter
* @param fx Element of the current class
* @param <Y> Target type
* @return transformed element through map
*/
<Y extends Type> EndoFunctor<Y> fmap(Function<X,Y> map, EndoFunctor<X> fx);
}
的东西
public class Id<X extends Type> implements EndoFunctor<X> {
protected X witness;
Id(X witness) { this.witness = witness; }
@Override
public <Y extends Type> Id<Y> fmap(Function<X, Y> map, Id<X> fx) {
return new Id<>(map.apply(fx.witness));
}
}
此代码存在的问题是Id<X>
与EndoFunctor<X>
类型不匹配。如何在fmap
界面中确定EndoFunctor
,以便K<T>
实施EndoFunctor<T>
任何类型T->U
并给出地图函数K<U>
,然后{{1}返回为一个值,没有任何类型转换(也就是说,因为我知道我的对象是Id<T>
,那么fmap
的结果必须是“Id<U>
,并且因此我将类型EndoFunctor<U>
的结果向下传播到这种类型)?
答案 0 :(得分:8)
您可以使用CRTP:
interface EndoFunctor<X extends Type, T extends EndoFunctor<X, T>> extends Type {
<Y extends Type> EndoFunctor<Y, ?> fmap(Function<X,Y> map, T fx);
}
class Id<X extends Type> implements EndoFunctor<X, Id<X>> {
protected X witness;
Id(X witness) { this.witness = witness; }
@Override
public <Y extends Type> Id<Y> fmap(Function<X, Y> map, Id<X> fx) {
return new Id<>(map.apply(fx.witness));
}
}
答案 1 :(得分:8)
如何在EndoFunctor界面中确定fmap,以便如果任何类型K实现EndoFunctor并且给出了地图函数T-> U,那么K将作为值返回,而不进行任何类型转换(也就是说,因为我知道我的对象是一个Id,那么fmap&#34;的结果必须是&#34;一个Id,因此我将EndoFunctor类型的结果向下转换为这样的类型)?
你不能;这被称为高级多态,而Java并不支持它(很少有语言可以支持)。 Jorn Vernee的回答让你尽可能地接近Java,但是那个界面允许你写
class NotId<X extends Type> implements EndoFunctor<X, Id<X>> {
@Override
public <Y extends Type> ADifferentEndoFunctorAgain<Y> fmap(Function<X, Y> map, Id<X> fx) { ... }
}
如果您想在EndoFunctor
s上编写通用代码,而不是使用{em>特定 EndoFunctor
Id
,那么就无法工作。
答案 2 :(得分:2)
问题不是Id<X>
与EndoFunctor<X>
不匹配,而是当您尝试覆盖fmap
时,您已使参数类型更具体,因此方法签名没有更长时间与fmap
EndoFunctor
的方法签名相匹配
这意味着其当前表单中的Id<X>
未完全实现EndoFunctor<X>
接口。在实现界面时,必须能够与您的班级进行互动,而无需知道它是一个不同的界面。
请遵循有关删除此方法参数和使用实例变量的注释中的建议,或将Id<X>
中的签名修改为public <Y extends Type> Id<Y> fmap(Function<X, Y> map, EndoFunctor<X> fx)
以使其与界面兼容。