使Java函数对象封装泛型函数的正确方法

时间:2012-10-26 02:41:20

标签: java generics metaprogramming functor

这是一个复杂但有希望有趣的问题,来自那些做了太多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,如果ab类型为E,则应用于f的{​​{1}}等于{{ 1}}已应用于(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>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()); 适用于两种不同的类型:gg。在具体示例中,我们希望E代表Rg

现在,上面Group<Integer>.plus(Integer,Integer)的实现不可行,因为对Group<Fraction>.plus(Fraction,Fraction)的两次调用之间的唯一区别是泛型类型,它被删除。所以,我会通过将域和范围添加为对象来稍微修改它:

checkCommutesWith

那么接口(g.ap)应该是什么样的?如果这是C ++,我会写相当于

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); }
}

但是AFAICT Java没有等效的模板模板参数。那就是我被困住的地方。

请注意,我不希望将Group包含为??签名的一部分,因为我希望能够将此代码与其他结构一起使用。我认为通用定义应该是可能的(对于“应该”的某些定义:))。

更新/澄清问题的关键在于集合之间的映射的定义属性是它们与eq()通信。组同态的定义属性是它们与plus()通信。环同态的定义属性是它们与times()通勤。我正在尝试定义一个通用的commutesWith函数来捕获这个想法,我正在寻找正确的抽象来封装eq,plus和times(以及其他结构)。

3 个答案:

答案 0 :(得分:1)

我不确定它对您的目的是否足够,但您可以检查JScience库中定义的类型结构。特别是,org.jscience.mathematics.structure中指定的接口可能代表一个有用的起点。

答案 1 :(得分:1)

我担心没有未经检查的强制转换,Java类型系统不够强大。 但是,它们的影响可以最小化。

考虑以Category-theoretical术语定义此内容。这通常足以表达所有事物,而群体同态是类别对象之间态射的具体案例。

有关以下代码的说明: 我将你的checkCommutesWith加入morphismTester关系。 2. eq现在是CategoryObject的属性,而不仅仅是参数。 3. castToConcreteObject的{​​{1}}用于进行未经检查的演员表。在我们的示例中,如果仅使用Category GroupsCategory并且GroupObject没有不同的实现,则这是安全的。

CategoryObject<GroupsCategory, E>

或许,要检查更多属性,您需要为每个属性定义一个类别。然后可能存在扩展其他几个类别的类别,这正是描述它们的对象的属性和它们之间的态射的原因。

答案 2 :(得分:1)

作为第一个近似值,请参阅

中的代码

https://github.com/rfqu/CodeSamples/blob/master/src/so/SoFun.java

至少它没有警告和错误编译:)