这是一个对 Haskell 和 C++ 模板元编程做过多的人提出的一个复杂但有趣的问题。请多多包涵
我正在编写一些通用 Java 代码来检查函数的某些代数属性,并且我在为其中一些提出适当的类型时遇到了一些困难。
作为一个有效的例子,这是一个检查函数是否可交换的函数:
<E, R>
boolean checkCommutative( Binary<E,E,R> f
, Binary<R,R,Boolean> eq
, E a, E b)
{
return eq.ap(f.ap(a,b), f.ap(b, a));
}
这个函数应该是这样的:“一个f接受两个E并产生一个的二元函数R是可交换的(相等性由eq比较两个的函数定义R),如果对于任何类型的a和,应用到等于应用到。”bEf(a,b)f(b,a)
C.plus(Integer,Integer)然后,我可以通过执行以下操作来测试给定函数是否可交换:
class Plus implements Binary<Integer, Integer, Integer> {
Integer ap(Integer a, Integer b) { return C.plus(a,b); }
}
class Eq implements Binary<Integer, Integer, Boolean> {
Boolean ap(Integer a, Integer b) { return a.equals(b); }
}
checkCommutative(new Plus(), new Eq(), rand(), rand());
一切都很好。
现在我想实现一些更复杂的东西。假设我有一个Group<E>带有 plus 方法的通用接口:
interface Group<E> {
E plus(E,E);
}
假设我有两个实现:TheIntegers和TheRationals:
class TheIntegers implements Group<Integer> { ... }
class TheRationals implements Group<Fraction> { ... }
现在,我希望能够捕捉到F从整数到有理数的通用函数与g类似Group.plus. 作为第一个剪辑,我想写这样的东西:
<E, R>
booleanCheckCommutesWith( Unary<E,R> f
, ?? g
, Binary<R,R,Boolean> eq
, E a, E b)
{
return eq.ap(f.ap(g.ap(a,b)), g.ap(f.ap(a), f.ap(b));
}
class F implements Unary<TheIntegers, TheRationals> {
Fraction ap (Integer x) { ... }
}
checkCommutesWith(new F(), new Plus(), new Eq(), rand(), rand());
这里的问题是类型应该是什么g?问题在于它g适用于两种不同的类型:E和R. 在具体示例中,我们希望g同时表示Group<Integer>.plus(Integer,Integer)和Group<Fraction>.plus(Fraction,Fraction)。
现在,checkCommutesWith上面的实现不可能工作,因为两个调用之间的唯一区别g.ap是泛型类型,它被删除了。因此,我将通过将域和范围添加为对象来对其进行一些修改:
boolean checkCommutesWith( Unary<DE,RE> f
, ?? g
, Binary<RE,RE,Boolean> eq
, S domain, S range
, E x, E y)
{
return eq.ap( f.ap(g.ap(domain, x, y)),
, g.ap(range, f.ap(x), f.ap(y))
);
}
class Plus implements ??
{
<E, G extends Group<E>>
E ap (G gp, E x, E y) { return gp.plus(x,y); }
}
那么界面(??)应该是什么样子的呢?如果这是 C++,我会写相当于
interface ?? <T> {
<E, S extends T<E>>
E ap(S, E, E);
}
但 AFAICT Java 没有模板模板参数的等效项。这就是我卡住的地方。
请注意,我不想将 Group 作为 签名的一部分checkCommutesWith,因为我也希望能够将此代码与其他结构一起使用。我认为应该可以有一个通用的定义(对于“应该”的一些定义:))。
更新/澄清这里问题的症结在于集合之间映射的定义属性是它们与 eq() 通勤。群同态的定义属性是它们与 plus() 交换。环同态的定义属性是它们与 times() 通勤。我正在尝试定义一个通用的 commutesWith 函数来捕捉这个想法,并且我正在寻找正确的抽象来封装 eq、plus 和 times(以及其他结构)。