Java支持咖喱吗?

Java支持咖喱吗?,java,functional-programming,currying,partial-application,Java,Functional Programming,Currying,Partial Application,我想知道是否有办法在Java中实现这一点。我认为没有闭包的本机支持是不可能的。在Java中完全可以使用curry和partial应用程序,但所需的代码量可能会让您感到厌烦 一些用Java演示咖喱和部分应用程序的代码: interface Function1<A, B> { public B apply(final A a); } interface Function2<A, B, C> { public C apply(final A a, final B b

我想知道是否有办法在Java中实现这一点。我认为没有闭包的本机支持是不可能的。

在Java中完全可以使用curry和partial应用程序,但所需的代码量可能会让您感到厌烦


一些用Java演示咖喱和部分应用程序的代码:

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

在Java中,curry方法始终是可能的,但它不以标准方式支持它。试图实现这一点很复杂,并且使代码非常难以阅读。Java不是合适的语言。

编辑:从2014年和Java 8开始,Java的函数式编程现在不仅是可能的,而且也不难看(我敢说很漂亮)。例如,见

旧答案:

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));
  }
}
如果要使用函数式编程技术,Java不是最佳选择。正如missingfaktor所写,您必须编写大量代码才能实现所需的功能


另一方面,您不局限于JVM上的Java—您可以使用或哪些是函数式语言(Scala实际上是函数式语言和OO)。

虽然您可以在Java中使用curry,但在Java中它很难看(因为它不受支持)。使用普通循环和简单表达式更简单、更快。如果您发布了一个使用咖喱的示例,我们可以建议使用与此相同的替代方法。

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

public interface Function<X,Z> {  // intention: f(X) -> Z
   public Z f(X x);
}
Java8(2014年3月18日发布)确实支持咖喱。发布的示例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));
   }
}
import java.util.function.*;
导入静态java.lang.System.out;
//使用JDK 1.8.0-ea-b75进行测试
公共类Curring和PartialFunction应用程序
{
公共静态void main(字符串[]args)
{
IntBinaryOperator simpleAdd=(a,b)->a+b;
IntFunction curriedAdd=a->b->a+b;
//演示简单添加:
out.println(simplead.applyAsInt(4,5));
//演示咖喱添加:
out.println(curriedAdd.apply(4.applyAsInt(5));
//Curried版本允许您执行部分应用程序:
IntUnaryOperator adder5=当前应用(5);
out.println(adder5.applyasit(4));
out.println(adder5.applyasit(6));
}
}

。。。这很好。就个人而言,有了Java8,我觉得没有什么理由使用Scala或Clojure等替代JVM语言。当然,它们提供了其他语言特性,但这还不足以证明转换成本和较弱的IDE/工具/库支持的合理性,依我看。

好吧,Scala、Clojure或Haskell(或任何其他函数式编程语言…)绝对是咖喱和其他函数技巧使用的语言

话虽如此,当然可以在不使用大量样板文件的情况下使用Java(当然,必须明确说明类型会带来很大的伤害-只需看看
curried
示例;-))

下面的测试展示了这两者,将
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);
}
@测试
public void shouldCurryFunction()引发异常{
//给定
函数3 func=(a,b,c)->a+b+c;
//什么时候
函数cur=当前值(func);
//然后
功能步骤1=当前应用(1);
功能步骤2=步骤1.应用(2);
整数结果=步骤2.应用(3);
(结果)isEqualTo(6);
}
以及部分应用程序,尽管在本例中不是真正的类型安全:

@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);
}
@测试
public void shouldCurryOneArgument()引发异常{
//给定
函数3加法=(a,b,c)->a+b+c;
//什么时候
函数2 curried=applyPartial(添加,,,put(1));
//然后
整数get=curried.apply(0,0);
资产(got)。isEqualTo(1);
}
这是从一个概念证明中提取出来的,我只是为了好玩才在明天的JavaOne之前一个小时内实现的,“因为我很无聊”;-)代码可在以下位置获得:

总体思路可以相对容易地扩展到FunctionN=>FunctionM,尽管“真正的类型安全性”对于partia应用程序示例来说仍然是一个问题,而currying示例将需要大量jcurry中的样板代码,但这是可行的


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

可以用Java 7 MethodHandles模拟咖喱:


使用Java8有很多选择。函数类型Javaslang和jOOλ都提供了现成的curry(我认为这是JDK中的一个疏忽),并且有一组用于curry 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);

关于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;
bifunctionadd=(x,y)->x+y;
函数增量=y->add.apply(1,y);
断言增量。应用(5)=6;
您还可以定义如下实用程序方法:

static <A1, A2, R> Function<A2, R> curry(BiFunction<A1, A2, R> f, A1 a1) {
    return a2 -> f.apply(a1, a2);
}
静态函数curry(双函数f,A1){
返回a2->f.apply(a1,a2);
}
这为您提供了更具可读性的语法:

Function<Integer, Integer> increment = curry(add, 1);
assert increment.apply(5) == 6;
函数增量=咖喱(添加,1);
断言增量。应用(5)=6;

Java 6还有另一种选择+

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{
私出结果;
私有布尔就绪=假;
公共布尔值isReady(){
准备好返回;
}
public Out getResult(){
返回结果;
}
受保护的无效设置结果(输出结果){
if(isReady()){
返回;
}
就绪=正确;
this.result=结果;
}
受保护的CurFun getReadyCurFun(){
最终输出最终结果=getResult();
返回新的CurFun(){
@凌驾
公共布尔值isReady(){
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;
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();
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

    }

}
curriedAdd.apply(1).apply(2) //in Java
//is equivalent to 
curriedAdd(1)(2) // in JS
    package math;

    import static java.lang.Math.*;
    import java.util.Optional;
    import java.util.function.*;

    public class UnivarDerivative
    {
      interface Approximation extends Function<Function<Double,Double>, 
      Function<Double,UnaryOperator<Double>>> {}
      public static void main(String[] args)
      {
        Approximation derivative = f->h->x->(f.apply(x+h)-f.apply(x))/h;
        double h=0.00001f;
        Optional<Double> d1=Optional.of(derivative.apply(x->1/x).apply(h).apply(1.0)); 
        Optional<Double> d2=Optional.of(
        derivative.apply(x->(1/sqrt(2*PI))*exp(-0.5*pow(x,2))).apply(h).apply(-0.00001));
        d1.ifPresent(System.out::println); //prints -0.9999900000988401
        d2.ifPresent(System.out::println); //prints 1.994710003159016E-6
      }
    }
public final class Currying {
  private static final Function<String, Consumer<String>> MAILER = (String ipAddress) -> (String message) -> {
    System.out.println(message + ":" + ipAddress );
  };
  //Currying
  private static final Consumer<String> LOCAL_MAILER =  MAILER.apply("127.0.0.1");

  public static void main(String[] args) {
      MAILER.apply("127.1.1.2").accept("Hello !!!!");
      LOCAL_MAILER.accept("Hello");
  }
}
private static <A, B, C> Function<A, Function<B, C>> Curry(BiFunction<A, B, C> f) {
    return a -> b -> f.apply(a, b);
}