Java是否支持Currying?

我想知道是否有任何方式来拉动Java。 我认为,如果没有本地支持封锁,这是不可能的。

Java 8(2014年3月18日发布)确实支持currying。 missingfaktor答案中发布的示例Java代码可以重写为:

import java.util.function.*; import static java.lang.System.out; // Tested with JDK 1.8.0-ea-b75 public class CurryingAndPartialFunctionApplication { public static void main(String[] args) { IntBinaryOperator simpleAdd = (a, b) -> a + b; IntFunction<IntUnaryOperator> curriedAdd = a -> b -> a + b; // Demonstrating simple add: out.println(simpleAdd.applyAsInt(4, 5)); // Demonstrating curried add: out.println(curriedAdd.apply(4).applyAsInt(5)); // Curried version lets you perform partial application: IntUnaryOperator adder5 = curriedAdd.apply(5); out.println(adder5.applyAsInt(4)); out.println(adder5.applyAsInt(6)); } } 

…这是相当不错的。 就个人而言,在Java 8可用的情况下,我没有理由使用诸如Scala或Clojure之类的替代JVM语言。 当然,它们提供了其他语言function,但这还不足以certificate转换成本和较弱的IDE /工具/库支持IMO。

在Java中,压缩和部分应用程序是完全可能的,但所需的代码量可能会让你失望。


一些在Java中演示currying和部分应用的代码:

 interface Function1<A, B> { public B apply(final A a); } interface Function2<A, B, C> { public C apply(final A a, final B b); } class Main { public static Function2<Integer, Integer, Integer> simpleAdd = new Function2<Integer, Integer, Integer>() { public Integer apply(final Integer a, final Integer b) { return a + b; } }; public static Function1<Integer, Function1<Integer, Integer>> curriedAdd = new Function1<Integer, Function1<Integer, Integer>>() { public Function1<Integer, Integer> apply(final Integer a) { return new Function1<Integer, Integer>() { public Integer apply(final Integer b) { return a + b; } }; } }; public static void main(String[] args) { // Demonstrating simple `add` System.out.println(simpleAdd.apply(4, 5)); // Demonstrating curried `add` System.out.println(curriedAdd.apply(4).apply(5)); // Curried version lets you perform partial application // as demonstrated below. Function1<Integer, Integer> adder5 = curriedAdd.apply(5); System.out.println(adder5.apply(4)); System.out.println(adder5.apply(6)); } } 

这里的FWIW是上述Java代码的Haskell等价物:

 simpleAdd :: (Int, Int) -> Int simpleAdd (a, b) = a + b curriedAdd :: Int -> Int -> Int curriedAdd ab = a + b main = do -- Demonstrating simpleAdd print $ simpleAdd (5, 4) -- Demonstrating curriedAdd print $ curriedAdd 5 4 -- Demostrating partial application let adder5 = curriedAdd 5 in do print $ adder5 6 print $ adder5 9 

编辑 :截至2014年和Java 8,在Javafunction编程现在不仅可能,但也不丑(我敢说,美丽)。 例如见罗杰里奥的答案 。

老答案:

如果你打算使用函数式编程技术,Java并不是最好的select。 正如missingfaktor写的,你将不得不编写相当多的代码来实现你想要的。

另一方面,你并不限于JVM上的Java–你可以使用Scala或Clojure这些函数式语言(事实上,Scala是function性的,也是面向对象的)。

Curry和Java 8有很多选项。函数typesJavaslang和jOOλ都提供了对盒子的控制(我认为这是JDK中的一个疏忽),而Cyclops function模块有一组静态方法用于Currying JDK函数和方法参考。 例如

  Curry.curry4(this::four).apply(3).apply(2).apply("three").apply("4"); public String four(Integer a,Integer b,String name,String postfix){ return name + (a*b) + postfix; } 

“咖喱”也适用于消费者。 例如,要返回一个有3个参数的方法,其中2个已经被应用,我们做类似的事情

  return CurryConsumer.curryC3(this::methodForSideEffects).apply(2).apply(2); 

的Javadoc

柯里要求返回一个函数 。 这是不可能与Java(没有函数指针),但我们可以定义并返回一个types,其中包含一个函数方法:

 public interface Function<X,Z> { // intention: f(X) -> Z public Z f(X x); } 

现在让我们来简单的划分一下。 我们需要一个分频器

 // f(X) -> Z public class Divider implements Function<Double, Double> { private double divisor; public Divider(double divisor) {this.divisor = divisor;} @Override public Double f(Double x) { return x/divisor; } } 

和一个DivideFunction

 // f(x) -> g public class DivideFunction implements Function<Double, Function<Double, Double>> { @Override public function<Double, Double> f(Double x) { return new Divider(x); } 

现在我们可以做一个咖喱师:

 DivideFunction divide = new DivideFunction(); double result = divide.f(2.).f(1.); // calculates f(1,2) = 0.5 

那么, 斯卡拉 ,Clojure或Haskell(或任何其他function编程语言…)绝对是用于咖喱和其他function技巧的语言。

有这样的说法,当然可以用Java来压缩Java,而不需要人们可能期望的超大量的样板文件(好吧,必须明确说明types会受到很多伤害 – 只要看看curried例子;-))。

下面的testing展示了将Function3卷入Function1 => Function1 => Function1

 @Test public void shouldCurryFunction() throws Exception { // given Function3<Integer, Integer, Integer, Integer> func = (a, b, c) -> a + b + c; // when Function<Integer, Function<Integer, Function<Integer, Integer>>> cur = curried(func); // then Function<Integer, Function<Integer, Integer>> step1 = cur.apply(1); Function<Integer, Integer> step2 = step1.apply(2); Integer result = step2.apply(3); assertThat(result).isEqualTo(6); } 

以及部分应用 ,虽然在这个例子中它不是真正的types安全:

 @Test public void shouldCurryOneArgument() throws Exception { // given Function3<Integer, Integer, Integer, Integer> adding = (a, b, c) -> a + b + c; // when Function2<Integer, Integer, Integer> curried = applyPartial(adding, _, _, put(1)); // then Integer got = curried.apply(0, 0); assertThat(got).isEqualTo(1); } 

这是从一个概念certificate,我刚刚在一个小时之前实现了JavaOne之前的乐趣“因为我很无聊”;-)代码可在这里: https : //github.com/ktoso/jcurry

总体思路可以比较容易地扩展到FunctionN => FunctionM,虽然“真实的types安全”仍然是一个偏袒应用示例的问题,而且curry的例子在jcurry中需要很多的代码,但这是可行的。

总而言之,这是可行的,但在斯卡拉它是开箱即用;-)

可以用Java 7 MethodHandles来模拟currying: http ://www.tutorials.de/threads/java-7-currying-mit-methodhandles.392397/

 import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; public class MethodHandleCurryingExample { public static void main(String[] args) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle sum = lookup.findStatic(Integer.class, "sum", MethodType.methodType(int.class, new Class[]{int.class, int.class})); //Currying MethodHandle plus1 = MethodHandles.insertArguments(sum,0,1); int result = (int) plus1.invokeExact(2); System.out.println(result); // Output: 3 } } 

在Java中总是可以使用一种方法,但是它并不以标准的方式支持它。 试图做到这一点很复杂,并使代码相当不可读。 Java不是适合这个的语言。

Java 6+是另一种select

 abstract class CurFun<Out> { private Out result; private boolean ready = false; public boolean isReady() { return ready; } public Out getResult() { return result; } protected void setResult(Out result) { if (isReady()) { return; } ready = true; this.result = result; } protected CurFun<Out> getReadyCurFun() { final Out finalResult = getResult(); return new CurFun<Out>() { @Override public boolean isReady() { return true; } @Override protected CurFun<Out> apply(Object value) { return getReadyCurFun(); } @Override public Out getResult() { return finalResult; } }; } protected abstract CurFun<Out> apply(final Object value); } 

那么你可以通过这种方式实现咖喱

 CurFun<String> curFun = new CurFun<String>() { @Override protected CurFun<String> apply(final Object value1) { return new CurFun<String>() { @Override protected CurFun<String> apply(final Object value2) { return new CurFun<String>() { @Override protected CurFun<String> apply(Object value3) { setResult(String.format("%s%s%s", value1, value2, value3)); // return null; return getReadyCurFun(); } }; } }; } }; CurFun<String> recur = curFun.apply("1"); CurFun<String> next = recur; int i = 2; while(next != null && (! next.isReady())) { recur = next; next = recur.apply(""+i); i++; } // The result would be "123" String result = recur.getResult(); 

虽然你可以在Java中进行Currying,但是它是丑陋的(因为它不被支持)。在Java中使用简单的循环和简单的expression式更简单快捷。 如果你发表一个你会使用currying的例子,我们可以build议做同样的事情的替代品。

Java 8的另一个可能性是:

 BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y; Function<Integer, Integer> increment = y -> add.apply(1, y); assert increment.apply(5) == 6; 

你也可以定义像这样的工具方法:

 static <A1, A2, R> Function<A2, R> curry(BiFunction<A1, A2, R> f, A1 a1) { return a2 -> f.apply(a1, a2); } 

这给你一个可以说是更可读的语法:

 Function<Integer, Integer> increment = curry(add, 1); assert increment.apply(5) == 6;