Java支持Currying吗?

use*_*855 87 java functional-programming currying partial-application

我想知道是否有任何方法可以在Java中提取它.我认为没有原生支持闭包是不可能的.

Rog*_*rio 141

Java 8(2014年3月18日发布)确实支持currying.missfaktor答案中发布的示例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));
   }
}
Run Code Online (Sandbox Code Playgroud)

......非常好.就个人而言,在Java 8可用的情况下,我认为没有理由使用其他JVM语言,例如Scala或Clojure.当然,它们提供其他语言功能,但这还不足以证明过渡成本和较弱的IDE /工具/库支持IMO.

  • 我对Java 8印象深刻,但Clojure是一个超越功能语言功能的引人注目的平台.Clojure提供高效,不可变的数据结构和复杂的并发技术,如软件事务内存. (11认同)
  • @ M4ks问题只是Java是否支持currying,与其他语言相比,它不是代码量. (7认同)
  • Clojure可能是一种很好的语言,但问题是它对于大多数只用于传统C风格语法的Java开发人员来说太"陌生"了; 将来很难看到Clojure(或任何其他替代JVM语言)的重大迁移,特别是考虑到多年来已经存在多种这样的语言而且还没有发生(在.NET世界中出现同样的现象,像F#这样的语言仍然是边缘的. (5认同)
  • 感谢您的解决方案,而不是语言 dig :) 较弱的 IDE 支持是一个问题,但工具/库并不清楚 - 在 Clojure 之后回到 Java 8,我真的很想念工具 midje,像 core 这样的库.async,以及宏和简单的函数式语法等语言特性。clojure 中的柯里化示例:`(def adder5 (partial + 5)) (prn (adder5 4)) (prn adder5 6)` (2认同)
  • 我不得不投反对票,因为它显示了简单的情况。尝试从 String 创建自己的类,然后将其转换为其他类,并比较代码量 (2认同)
  • 实际上有很多不使用 Java 的原因:代码易读性差、原语混乱、没有模式匹配、没有 case 类、没有真正的闭包、冗长、没有默认参数、没有类型推断、没有隐式值、默认情况下不可变、不可变通常是通过引用而不是结构,没有好的并发模型,没有元组的字面定义等。如果可以选择,请选择 Scala。如果你不能,那么 Java 8 也不错。 (2认同)

mis*_*tor 66

在Java中绝对可以使用Currying和部分应用程序,但是所需的代码量可能会让您失望.


一些代码用于演示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));
  }
}
Run Code Online (Sandbox Code Playgroud)

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

simpleAdd :: (Int, Int) -> Int
simpleAdd (a, b) = a + b

curriedAdd :: Int -> Int -> Int
curriedAdd a b = 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
Run Code Online (Sandbox Code Playgroud)

  • 这样丑陋的java代码.+1,你已经赢了. (15认同)
  • 自Java 8发布以来,这个答案已经过时了.请参阅Rogério的答案以获得更简洁的方法. (14认同)

Joh*_*ean 14

Currying with Java 8有很多选项.函数类型Javaslang和jOOλ都提供开箱即用的Currying(我认为这是JDK中的疏忽),Cyclops 函数模块有一组用于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;
 }
Run Code Online (Sandbox Code Playgroud)

"Currying"也适用于消费者.例如,为了返回一个带有3个参数的方法,以及其中2个已经应用的方法,我们会做类似的事情

 return CurryConsumer.curryC3(this::methodForSideEffects).apply(2).apply(2);
Run Code Online (Sandbox Code Playgroud)

的Javadoc


Xae*_*ess 13

编辑:截至2014年和Java 8,Java中的函数式编程现在不仅可行,而且也不丑(我敢说很漂亮).例如,参见Rogerio的答案.

老答案:

如果要使用函数式编程技术,Java不是最佳选择.正如missingfaktor所写,你将不得不编写相当多的代码来实现你想要的.

另一方面,您不限于JVM上的Java - 您可以使用ScalaClojure作为函数式语言(事实上,Scala既是功能性的又是OO).


And*_*s_D 8

Currying需要返回一个函数.这是不可能的java(没有函数指针),但我们可以定义并返回包含函数方法的类型:

public interface Function<X,Z> {  // intention: f(X) -> Z
   public Z f(X x);
}
Run Code Online (Sandbox Code Playgroud)

现在让我们来巴结一个简单的除法.我们需要一个分频器:

// 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

和一个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);
  }
Run Code Online (Sandbox Code Playgroud)

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

DivideFunction divide = new DivideFunction();
double result = divide.f(2.).f(1.);  // calculates f(1,2) = 0.5
Run Code Online (Sandbox Code Playgroud)


Ris*_*wal 6

是的,请自行查看代码示例:

import java.util.function.Function;

public class Currying {

    private static Function<Integer, Function<Integer,Integer>> curriedAdd = a -> b -> a+b ;

    public static void main(String[] args) {

        //see partial application of parameters
        Function<Integer,Integer> curried = curriedAdd.apply(5);
        //This partial applied function can be later used as
        System.out.println("ans of curried add by partial application: "+ curried.apply(6));
        // ans is 11

        //JS example of curriedAdd(1)(3)
        System.out.println("ans of curried add: "+ curriedAdd.apply(1).apply(3));
        // ans is 4

    }

}
Run Code Online (Sandbox Code Playgroud)

这是一个简单的例子,curriedAdd是一个curried函数,它返回另一个函数,这可以用于部分应用存储在curried 中的参数,它本身就是一个函数。现在,当我们将其打印在屏幕上时,这将完全应用。

此外,稍后您可以看到如何以 JS 风格使用它

curriedAdd.apply(1).apply(2) //in Java
//is equivalent to 
curriedAdd(1)(2) // in JS
Run Code Online (Sandbox Code Playgroud)


Kon*_*ski 5

那么,斯卡拉,Clojure的或哈斯克尔(或任何其他函数式编程语言...)肯定是语言使用的柯里和其他功能的技巧.

说到这一点当然可以用Java来解决,而没有人们可能期望的大量样板(好吧,不得不明确地说这些类型会伤害很多 - 只需看看这个curried例子;-)).

测试波纹管展示两个,讨好一个Function3Function1 => 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);
}
Run Code Online (Sandbox Code Playgroud)

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

@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);
}
Run Code Online (Sandbox Code Playgroud)

这是从概念证明中获得的,我刚刚在JavaOne之前的一小时内实现了乐趣"因为我很无聊";-)代码可以在这里找到:https://github.com/ktoso/jcurry

一般的想法可以相对容易地扩展到FunctionN => FunctionM,尽管"真实的类型安全"仍然是partia应用程序示例的一个问题,并且currying示例需要在jcurry中使用大量的boilerplaty代码,但它是可行的.

总而言之,它是可行的,但在Scala它是开箱即用的;-)


Tho*_*ont 5

可以使用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
    }
}
Run Code Online (Sandbox Code Playgroud)