Java prend-il en charge le currying?

Je me demandais s’il y avait un moyen de tirer cela en Java. Je pense que ce n’est pas possible sans un support natif pour les fermetures.

Java 8 (publié le 18 mars 2014) prend en charge le currying. L’exemple de code Java publié dans la réponse par missingfaktor peut être réécrit comme suit :

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(Ssortingng[] args) { IntBinaryOperator simpleAdd = (a, b) -> a + b; IntFunction 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)); } } 

… ce qui est plutôt sympa. Personnellement, avec Java 8 disponible, je vois peu de raisons d’utiliser un autre langage JVM tel que Scala ou Clojure. Bien sûr, ils fournissent d’autres fonctionnalités de langage, mais cela ne suffit pas à justifier le coût de la transition et le support plus faible de l’IDE / de l’outillage / des bibliothèques, IMO.

Le currying et l’application partielle sont absolument possibles en Java, mais la quantité de code requirejse va probablement vous désactiver.


Un code pour démontrer l’application currying et partielle en Java:

 interface Function1 { public B apply(final A a); } interface Function2 { public C apply(final A a, final B b); } class Main { public static Function2 simpleAdd = new Function2() { public Integer apply(final Integer a, final Integer b) { return a + b; } }; public static Function1> curriedAdd = new Function1>() { public Function1 apply(final Integer a) { return new Function1() { public Integer apply(final Integer b) { return a + b; } }; } }; public static void main(Ssortingng[] 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 adder5 = curriedAdd.apply(5); System.out.println(adder5.apply(4)); System.out.println(adder5.apply(6)); } } 

FWIW voici l’équivalent Haskell du code Java ci-dessus:

 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 

EDIT : Depuis 2014 et Java 8, la functional programming en Java est désormais non seulement possible, mais également pas moche (j’ose dire beau). Voir par exemple la réponse de Rogerio .

Vieille réponse:

Java n’est pas le meilleur choix si vous utilisez des techniques de functional programming. Comme l’a écrit missingfaktor, vous devrez écrire beaucoup de code pour obtenir ce que vous voulez.

D’autre part, vous n’êtes pas limité à Java sur JVM – vous pouvez utiliser Scala ou Clojure qui sont des langages fonctionnels (Scala est en fait à la fois fonctionnel et OO).

Il y a beaucoup d’options pour Currying avec Java 8. Les fonctions Javaslang et JOOλ offrent toutes deux un Currying prêt à l’emploi (je pense que c’était un JDK) et le module Cyclops Functions dispose d’un ensemble de méthodes statiques pour Currying JDK et références de méthode. Par exemple

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

Le «currying» est également disponible pour les consommateurs. Par exemple, pour retourner une méthode avec 3 parameters, et 2 de ceux déjà appliqués, nous faisons quelque chose de similaire.

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

Javadoc

Le curry nécessite de renvoyer une fonction . Ceci n’est pas possible avec java (pas de pointeur de fonction) mais on peut définir et renvoyer un type contenant une méthode de fonction:

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

Maintenant, curry une simple division. Nous avons besoin d’un diviseur :

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

et une fonction DivideFunction :

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

Maintenant, nous pouvons faire une division au curry:

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

Eh bien, Scala , Clojure ou Haskell (ou tout autre langage de programmation fonctionnel …) sont certainement les langages à utiliser pour le curry et autres astuces fonctionnelles.

Cela étant, il est certainement possible de travailler avec Java sans les superplates que l’on pourrait attendre (eh bien, devoir être explicite à propos des types est très douloureux – il suffit de regarder l’exemple curried ;-)).

Les tests ci-dessous montrent les deux, en sélectionnant Function1 => Function1 => Function1 dans Function1 => Function1 => Function1 :

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

ainsi que l’application partielle , bien que ce ne soit pas vraiment sûr dans cet exemple:

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

Ceci est tiré d’un Proof Of Concept que je viens d’implémenter avant JavaOne demain dans une heure “parce que je m’ennuyais” 😉 Le code est disponible ici: https://github.com/ktoso/jcurry

L’idée générale pourrait être étendue à FunctionN => FunctionM, relativement facilement, bien que “real typesafety” rest un problème pour l’exemple d’application partia et l’exemple de currying nécessiterait énormément de code chainplaty dans jcurry , mais c’est faisable.

Dans l’ensemble, c’est faisable, pourtant dans Scala c’est sorti de la boîte 😉

On peut émuler le curry avec Java 7 MethodHandles: 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(Ssortingng[] 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 } } 

Le curry d’une méthode est toujours possible en Java, mais ne le prend pas en charge de manière standard. Essayer d’atteindre cet objective est compliqué et rend le code assez illisible. Java n’est pas le langage approprié pour cela.

Bien que vous puissiez faire du curry en Java, c’est moche (car ce n’est pas supporté) En Java, il est plus simple et plus rapide d’utiliser des boucles simples et des expressions simples. Si vous affichez un exemple où vous utiliseriez le curry, nous pouvons suggérer des alternatives qui font la même chose.

Un autre choix est ici pour Java 6+

 abstract class CurFun { 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 getReadyCurFun() { final Out finalResult = getResult(); return new CurFun() { @Override public boolean isReady() { return true; } @Override protected CurFun apply(Object value) { return getReadyCurFun(); } @Override public Out getResult() { return finalResult; } }; } protected abstract CurFun apply(final Object value); } 

alors vous pourriez réaliser le curry de cette façon

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

Oui, voir l’exemple de code pour vous-même:

 import java.util.function.Function; public class Currying { private static Function> curriedAdd = a -> b -> a+b ; public static void main(Ssortingng[] args) { //see partial application of parameters Function 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 } } 

Ceci est un exemple simple avec curriedAdd étant une fonction curry qui renvoie une autre fonction, qui peut être utilisée pour une application partielle des parameters stockés dans curried qui est une fonction en soi. Ceci est maintenant appliqué plus tard lorsque nous l’imprimons à l’écran.

De plus, plus tard, vous pouvez voir comment vous pouvez l’utiliser dans un style JS comme

 curriedAdd.apply(1).apply(2) //in Java //is equivalent to curriedAdd(1)(2) // in JS 

Une autre prise sur les possibilités de Java 8:

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

Vous pouvez également définir des méthodes utilitaires comme celle-ci:

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

Ce qui vous donne une syntaxe plus lisible:

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

Ceci est une bibliothèque pour le curry et l’application partielle en Java:

https://github.com/Ahmed-Adel-Ismail/J-Curry

Il prend également en charge la déstructuration de Tuples et Map.Entry en parameters de méthode, comme par exemple passer une Map.Entry à une méthode qui prend 2 parameters, de sorte que Entry.getKey () ira au premier paramètre, et à Entry.getValue () ira pour le deuxième paramètre

Plus de détails dans le fichier README