Java 8 Functional Style Programming, What is the Difference Between Currying and Composing Functions

I am new to the world of functional programming. Considering the new function style programming that ships with Java 8. I recently learned about currying and composing a method. Understanding the true essence of functional style programming using java is quite difficult, and now I have a couple of questions. However, before asking all these questions, I tried the same thing in python and am now a little familiar with a few basic concepts.

1. In java, how currying and composition are different. In fact, I see no difference whatsoever, especially after reading this article https://dzone.com/articles/higher-order-functions

2. As a programmar (from my perspective on Java programming), why do I prefer curry. for example, why should I do this f(x){ return g(y) } instead of f(x,y){ return x(y)} what's the difference?

+5
source share
3 answers

While both operations produce a function, the example makes the difference clear enough:

  • Currying takes a single function f() and creates an "intermediate" function f'() , which matches f() , but with some parameters already set. When you finally fill out the rest of the options, you will appreciate the original f() .
  • While the composition will take two functions f() and g() and create a completely different function g(f()) .

Take a simple example: f(x,y) = x+y , where x and y are integers. No amount and currying combination of this function can lead to a function that will ever return a non-integer result. But compose it with g(x) = x/2 , and you get g(f(x,y)) = (x+y)/2 , which, of course, will happily return non-integer numbers.

Why would you use currying?

Examples of Java instances, for example, are the result of a fairly similar process. Instance methods differ from static methods in that they have an additional hidden this parameter. When you say new Foo() , essentially, you bind this hidden parameter to the newly created Foo object. Therefore, instead of calling the void bar(Foo this, int x) function void bar(Foo this, int x) you can simply refer to it as void bar(int x) , with the first parameter already set in place. (By the way, void bar(Foo this, int x) is actually the correct Java syntax, we almost never use it.)

This is not entirely a coincidence, since pure functional languages โ€‹โ€‹can only have functions whose outputs depend only on its inputs (unlike OO languages, where the output of a method can also depend on the internal state of the object; the method belongs.)

As a general tip, if you want to learn the essence of functional programming, it is best not to do this with Java. Not even from Scala. Try to learn it from a pure functional language such as Haskell, and then you can return to Java and better understand which subset of FP was implemented in it and how.

+8
source

I would like to add code to a very nice explanation of @biziclop:

An example of currying in functional Java:

 BiFunction<Integer, Integer, IntFunction<Integer>> currying = (x, y) -> z -> x * y / z; System.out.println(currying.apply(5, 6).apply(2)); // 15 

As you can see, lambda is parameterized. In this example, we are trying to multiply 5 by 6, and then divide by 2.

The first apply(5) called and the variable x gets the value 5 , and the function becomes 5 * y / z

Then apply(6) invoked and the variable 'y' gets the value '6', and the function becomes 5 * 6 / z

Then apply(2) is called, and the variable 'z' gets the value '2', and the function becomes 5 * 6 / 2

As you can use currying, this method is little used in Java. Currying is useful in pure functional languages โ€‹โ€‹where functions are limited to a single argument and they benefit from currying, which converts a function that takes multiple arguments, so it can be called several times each with single arguments.

So how can you use currying in Java?

This is useful when you need to parameterize a function at several levels. For example, let's say we have several collections, each of which represents a different category, and we want to get certain elements from each category. The following is a simple example, given the two collections representing the recorded numbers, classified as ones and tens . Example:

 public class Currying { private static List<String> ones = Arrays.asList("Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"); private static List<String> tens = Arrays.asList("Zero", "Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"); public static Function<String, Function<Integer, String>> getNumbers() { return units -> number -> { return units == "Ones" ? ones.get(number % 10) : tens.get(number % 10); }; } public static void main(String[] args) { Function<String, Function<Integer, String>> currying = getNumbers(); System.out.println(currying.apply("Tens").apply(8)); // 80 System.out.println(currying.apply("Ones").apply(2)); // 2 } } 

In the above example, the currying function returns another function currying.apply("Ones").apply(2)) ;

The first apply("Tens") , and the units variable becomes tens

Then apply(2) is called, and the variable number becomes 8 retrieving 80 from the tens collection.

The same logic applies to currying.apply("Ones").apply(2)) .

+6
source

Currying is a way to create new functions by baking arguments to existing functions. This is usually done in languages โ€‹โ€‹such as Haskell, where the language syntax relies on it easily.

A typical example is the presence of a function (addTwoNumbers ab) , which adds two numbers in which currying must provide fewer arguments in order to get a function that takes the rest of the arguments to take action. For example, (addTwoNumbers 42) , where a is provided (42) but not b, is a function (not the result) that takes one argument (b) and returns 42 + b. Therefore ((addTwoNumbers 42) 10) will return 52.

As you can see, the syntax of the language should help this, so that it works well, and Java does not help much, so it does not appear in textbooks. The functional aspects of Java 8 basically boil down to avoiding loops in code using Streams and having enough predefined functions to use as lining with lambda expressions. They have a lazy mark in Streams, which is a very nice and excellent achievement, but the programmer didnโ€™t buy much in terms of expressiveness in the code.

See https://wiki.haskell.org/Currying for more details.

+4
source

Source: https://habr.com/ru/post/1270361/


All Articles