滥用泛型在Java中实现curried合成函数
因此,在对Java泛型进行了一些研究之后,为了更深入地了解它们的功能,我决定尝试实现函数式程序员熟悉的组合函数的curried版本。Compose的类型是(在函数式语言中)滥用泛型在Java中实现curried合成函数,java,generics,functional-programming,currying,Java,Generics,Functional Programming,Currying,因此,在对Java泛型进行了一些研究之后,为了更深入地了解它们的功能,我决定尝试实现函数式程序员熟悉的组合函数的curried版本。Compose的类型是(在函数式语言中)(b->c)->(a->b)->(a->c)。编写算术函数并不难,因为它们只是多态的,但compose是一个更高阶的函数,这对我理解Java中的泛型来说是一个累赘 以下是我迄今为止创建的实现: public class Currying { public static void main(String[] argv){
(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);
}
公共类咖喱{
公共静态void main(字符串[]argv){
//咖喱的基本用法
System.out.println(add().ap(3.ap(4));
//接下来,让我们试试(3*4)+2
//首先让我们创建(+2)函数。。。
Fn plus2=add().ap(2);
//接下来是times 3函数
Fn times3=mult().ap(3);
//现在我们把它们组合成一个乘2加3的函数
Fn times3plus2=compose().ap(plus2).ap(times3);
//现在我们可以输入最后一个参数并打印结果
//无需撰写:
System.out.println(plus2.ap(times3.ap(4));
//与撰写:
System.out.println(times3plus2.ap(新整数(4));
}
公共静电
Fn c)->--f
Fn b)->--g
Fn>>/(a->c)
撰写(){
返回新的Fn(){
公共Fn ap(最终Fn f){
返回新的Fn(){
公共Fn ap(最终Fn g){
返回新的Fn(){
公共C ap(最终A){
返回f.ap(g.ap(a));
}
};
}
};
}
};
}
//咖喱加成
公共静态Fn add(){
返回新的Fn(){
公共Fn ap(最终整数a){
返回新的Fn(){
公共整数ap(最终整数b){
返回a+b;
}
};
}
};
}
//咖喱乘法
公共静态Fn mult(){
返回新的Fn(){
公共Fn ap(最终整数a){
返回新的Fn(){
公共整数ap(最终整数b){
返回a*b;
}
};
}
};
}
}
接口Fn{
公共B ap(最终A);
}
add、mult和compose的实现都可以很好地编译,但我发现自己在实际使用compose时遇到了一个问题。第12行出现以下错误(在main中首次使用compose):
java:12:ap(Fn)in
Fn
无法应用于(Fn)
Fn times3plus2=compose().ap(plus2).ap(times3);
我假设这个错误是因为泛型类型是不变的,但我不确定如何解决这个问题。据我所知,通配符类型的变量在某些情况下可以用来减轻不变性,但我不确定如何在这里使用它,甚至不知道它是否有用
免责声明:我无意在任何实际项目中编写这样的代码。这是一件有趣的“能做到”的事情。此外,我不顾标准Java实践,简化了变量名,因为否则这个示例将变得更加难以理解。这里的基本问题是,在最初调用
compose()
时,编译器无法推断A、B和C的绑定,所以它假设它们都是对象。可以通过显式指定类型绑定来修复它:
Fn<Integer, Integer> times3plus2 =
Currying.<Integer, Integer, Integer>compose().ap(plus2).ap(times3);
Fn times3plus2=
Currying.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));
}
};
}
}
public static ComposeStart compose(){
返回新的ComposeStart();
}
类ComposeStart{
公共合成继续ap(Fn f){
返回新组件继续(f);
}
}
类复合继续{
私人最终Fn f;
复合连续性(Fn f){
这个。f=f;
}
公共Fn ap(最终Fn g){
返回新的Fn(){
公共行政助理(甲){
返回f.ap(g.ap(a));
}
};
}
}
然而,接下来的中间步骤不再是
Fn
s.多亏了Russell Zahniser的洞察力,我没有给Java提供足够的信息,所以我稍微改变了布局,以便我们实例化一个“Composer”对象,并填入适当的类型变量。以下是我目前的工作方案:
interface Fn<A, B> {
public B ap(final A a);
}
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 = new Composer<Integer,Integer,Integer>()
.compose().ap(plus2).ap(times3);
// without compose
System.out.println(plus2.ap(times3.ap(4)));
// with compose
System.out.println(times3plus2.ap(4));
}
static class Composer<A,B,C> {
public
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));
}
};
}
};
}
};
}
}
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;
}
};
}
};
}
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;
}
};
}
};
}
}
接口Fn{
公共B ap(最终A);
}
公共课咖喱{
公共静态void main(字符串[]argv){
//咖喱的基本用法
System.out.println(add().ap(3.ap(4));
//接下来,让我们试试(3*4)+2
//首先让我们创建(+2)函数。。。
Fn plus2=add().ap(2);
//接下来是times 3函数
Fn times3=mult().ap(3);
//现在我们把它们组合成一个乘2加3的函数
Fn times3plus2=新作曲家()
.compose().ap(plus2).ap(times3);
//不假思索
System.out.println(plus2.ap(times3.ap(4));
//用作曲
System.out.println(times3plus2.ap(4));
}
静态类生成器{
公众的
Fn c)->--f
Fn b)->--g
Fn>>/(a->c)
撰写(){
返回新的Fn(){
公共Fn ap(最终Fn f){
返回新的Fn(){
公共Fn ap(最终Fn g){
返回新的Fn(){
公共C ap(最终A){
返回f.ap(g.ap(a));
}
};
}
};
}
};
}
}
公共静态Fn add(){
返回新的Fn(){
公共Fn ap(最终整数a){
返回新的Fn(){
公共整数ap(最终整数b){
返回a+b;
}
};
}
};
}
公共静态Fn mult(){
返回新的Fn(){
公共Fn ap(最终整数a){
返回新的Fn(){
公共整数ap(最终整数b){
interface Fn<A, B> {
public B ap(final A a);
}
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 = new Composer<Integer,Integer,Integer>()
.compose().ap(plus2).ap(times3);
// without compose
System.out.println(plus2.ap(times3.ap(4)));
// with compose
System.out.println(times3plus2.ap(4));
}
static class Composer<A,B,C> {
public
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));
}
};
}
};
}
};
}
}
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;
}
};
}
};
}
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;
}
};
}
};
}
}
public static final <X, Y> Chainer<X, Y> chain(
final Function<X, Y> primary
) {
return new Chainer<X, Y>(primary);
}
private static final class FunctionChain<IN, OUT> implements Function<IN, OUT> {
@SuppressWarnings("rawtypes")
private final List<Function> chain = new LinkedList<Function>();
private FunctionChain(@SuppressWarnings("rawtypes") final List<Function> chain) {
this.chain.addAll(chain);
}
@SuppressWarnings("unchecked")
@Override
public OUT apply(final IN in) {
Object ret = in;
for (final Function<Object, Object> f : chain) {
ret = f.apply(ret);
}
return (OUT) ret;
}
}
public static final class Chainer<IN, OUT> {
@SuppressWarnings("rawtypes")
private final LinkedList<Function> functions = new LinkedList<Function>();
@SuppressWarnings("unchecked")
private Chainer(@SuppressWarnings("rawtypes") final Function func) {
then(func);
}
@SuppressWarnings("unchecked")
public <OUT2> Chainer<IN, OUT2> then(final Function<OUT, OUT2> func) {
if (func instanceof FunctionChain) {
functions.addAll(((FunctionChain<?, ?>)func).chain);
} else {
functions.add(func);
}
return (Chainer<IN, OUT2>) this;
}
@SuppressWarnings("unchecked")
public Function<IN, OUT> build() {
// If empty, it's a noop function. If one element, there's no need for a chain.
return new FunctionChain<IN, OUT>(functions);
}
}
public static final <X, Y, Z> Function<X, Z> combine(
final Function<X, Y> primary,
final Function<Y, Z> secondary
) {
return chain(primary).then(secondary).build();
}