gpt4 book ai didi

java - 滥用泛型在 Java 中实现柯里化(Currying)组合函数

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:22:45 30 4
gpt4 key购买 nike

因此,在稍微接触了 Java 泛型之后,为了更深入地了解它们的功能,我决定尝试实现函数式程序员熟悉的组合函数的柯里化(Currying)版本。 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);
}

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 惯例,使变量名变得简短,否则这个示例将变成甚至难以理解的文本墙。

最佳答案

这里的基本问题是,在对 compose() 的原始调用中,编译器无法推断 A、B 和 C 的绑定(bind),因此它假设它们都成为对象。您可以通过明确指定类型绑定(bind)来修复它:

Fn<Integer, Integer> times3plus2 = 
Currying.<Integer, Integer, Integer>compose().ap(plus2).ap(times3);

当然,您会失去类型推断带来的清晰度。如果您需要类型推断,您可以定义一些中间类来进行推断:

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

但是,柯里化(Currying)的中间步骤不再是 Fn

关于java - 滥用泛型在 Java 中实现柯里化(Currying)组合函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8913900/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com