deo*_*ian 6 java generics functional-programming currying
因此,在使用Java泛型之后,为了更深入地了解它们的功能,我决定尝试实现组合函数的curried版本,熟悉函数式程序员.Compose具有类型(在函数语言中)(b -> c) -> (a -> b) -> (a -> c).做currying算术函数并不太难,因为它们只是多态的,但是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);
}
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
我认为这个错误是因为泛型类型是不变的,但我不知道如何解决这个问题.根据我的阅读,通配符类型变量可用于减轻某些情况下的不变性,但我不确定如何在此使用它,甚至是否有用.
免责声明:我无意在任何实际项目中编写这样的代码.这是一种有趣的"可以做到"的事情.此外,我无视标准Java实践使变量名称变得简短,因为否则这个例子变成了一个更难以理解的文本墙.
这里的基本问题是,在对 的原始调用中compose(),编译器无法推断 A、B 和 C 的绑定,因此它假设它们都是 Object。您可以通过显式指定类型绑定来修复它:
Fn<Integer, Integer> times3plus2 =
Currying.<Integer, Integer, Integer>compose().ap(plus2).ap(times3);
Run Code Online (Sandbox Code Playgroud)
当然,这样你就失去了类型推断带来的清晰度。如果您需要类型推断,您可以定义一些中间类来进行推断:
public static ComposeStart compose() {
return new ComposeStart();
}
class ComposeStart {
public <B,C> ComposeContinuation<B,C> ap(Fn<B,C> f) {
return new ComposeContinuation<B, C>(f);
}
}
class ComposeContinuation<B, C> {
private final Fn<B,C> f;
ComposeContinuation(Fn<B,C> f) {
this.f = f;
}
public <A> Fn<A,C> ap(final Fn<A,B> g) {
return new Fn<A,C>() {
public C ap(A a) {
return f.ap(g.ap(a));
}
};
}
}
Run Code Online (Sandbox Code Playgroud)
然而,柯里化的中间步骤就不再是Fn问题了。
| 归档时间: |
|
| 查看次数: |
1070 次 |
| 最近记录: |