【发布时间】:2012-02-13 09:28:18
【问题描述】:
所以,在玩了一点 Java 泛型之后,为了更深入地了解它们的功能,我决定尝试实现函数式程序员熟悉的组合函数的柯里化版本。 Compose 的类型(在函数式语言中)(b -> c) -> (a -> b) -> (a -> c)。做柯里化算术函数并不太难,因为它们只是多态的,但 compose 是一个高阶函数,事实证明它对我对 Java 泛型的理解很费力。
这是我目前创建的实现:
public class Currying {
public static void main(String[] argv){
// Basic usage of currying
System.out.println(add().ap(3).ap(4));
// Next, lets try (3 * 4) + 2
// First lets create the (+2) function...
Fn<Integer, Integer> plus2 = add().ap(2);
// next, the times 3 function
Fn<Integer, Integer> times3 = mult().ap(3);
// now we compose them into a multiply by 2 and add 3 function
Fn<Integer, Integer> times3plus2 = compose().ap(plus2).ap(times3);
// now we can put in the final argument and print the result
// without compose:
System.out.println(plus2.ap(times3.ap(4)));
// with compose:
System.out.println(times3plus2.ap(new Integer(4)));
}
public static <A,B,C>
Fn<Fn<B,C>, // (b -> c) -> -- f
Fn<Fn<A,B>, // (a -> b) -> -- g
Fn<A,C>>> // (a -> c)
compose(){
return new Fn<Fn<B,C>,
Fn<Fn<A,B>,
Fn<A,C>>> () {
public Fn<Fn<A,B>,
Fn<A,C>> ap(final Fn<B,C> f){
return new Fn<Fn<A,B>,
Fn<A,C>>() {
public Fn<A,C> ap(final Fn<A,B> g){
return new Fn<A,C>(){
public C ap(final A a){
return f.ap(g.ap(a));
}
};
}
};
}
};
}
// curried addition
public static Fn<Integer, Fn<Integer, Integer>> add(){
return new Fn<Integer, Fn<Integer, Integer>>(){
public Fn<Integer,Integer> ap(final Integer a) {
return new Fn<Integer, Integer>() {
public Integer ap(final Integer b){
return a + b;
}
};
}
};
}
// curried multiplication
public static Fn<Integer, Fn<Integer, Integer>> mult(){
return new Fn<Integer, Fn<Integer, Integer>>(){
public Fn<Integer,Integer> ap(final Integer a) {
return new Fn<Integer, Integer>() {
public Integer ap(final Integer b){
return a * b;
}
};
}
};
}
}
interface Fn<A, B> {
public B ap(final A a);
}
add、mult 和 compose 的实现都编译得很好,但我发现自己在实际使用 compose 时遇到了问题。第 12 行出现以下错误(第一次在 main 中使用 compose):
Currying.java:12: ap(Fn<java.lang.Object,java.lang.Object>) in
Fn<Fn<java.lang.Object,java.lang.Object>,Fn<Fn<java.lang.Object,java.lang.Object>,Fn<java.lang.Object,java.lang.Object>>>
cannot be applied to (Fn<java.lang.Integer,java.lang.Integer>)
Fn<Integer,Integer> times3plus2 = compose().ap(plus2).ap(times3);
我认为这个错误是因为泛型类型是不变的,但我不知道如何解决这个问题。根据我的阅读,通配符类型变量在某些情况下可用于缓解不变性,但我不确定如何在这里使用它,甚至不确定它是否有用。
免责声明:我无意在任何实际项目中编写这样的代码。这是一个有趣的“可以做到”的事情。此外,我无视标准 Java 实践,使变量名称简短化,因为否则此示例将成为甚至一堵难以理解的文本墙。
【问题讨论】:
标签: java generics functional-programming currying