Currying is just a name for a function, which does not have all the arguments passed to it.
So, if have a function which takes 4 arguments, we will provide only 3 and we will provide 1 later on.
This stage of function, where it does not have all the arguments is called curried stage, I made up this stage .. haha
But, putting a function in a stage, where it does not have all the arguments and we can pass the function around or use it as an object. This process is called currying.
You can watch my youtube video to see , how it is done.
I have two data structures a List of Strings, and Map of String to String.
List -> final List<String> listOfColumnNames = List.of("FirstName", "LastName", "FatherName");
Map -> final Map<String, String> mapOfColumnAndValue = new HashMap<>();
And, I need to do an operation, where I have put some default value for each column .
I am taking an example, where I have to put default values for those columns, for which no value was provided by the user.
So, I can do this operation explicitly or you can say that it is done in an imperative way.
final String defaultValue = "NA"; for(final String columnName : listOfColumnNames){ mapOfColumnAndValue.put(columnName, defaultValue); }
Loop over the list of columns and put default value in it. But, In the cases, where we have different default values for different scenarios, then how are we going to do it.
Create different functions for all the default cases, or a function which takes an argument for default value.
Or we can do currying to isolate entire process.
private final static BiFunction<Map<String, String>, String, Consumer<String>> curryAMap = (Map<String, String> map, String defaultValue) -> ((Consumer<String>) (String columnValue) -> map.put(columnValue, defaultValue));
In the above code, I have created a BiFunction object , which takes a HashMap object and a defaultValue, and returns a Consumer object. And, this consumer object puts the default values in the all the keys passed to it.
So, curryMap will returned a curried map, whose default value is set forever. We can not change that default value. We can have different curried objects for the same map, with different default value.
We can remove the type information from the lambda to make look a lot simpler.
private final static BiFunction<Map<String, String>, String, Consumer<String>> curryAMapSmaller= (map, defaultValue) -> (columnValue) -> map.put(columnValue, defaultValue);
As, we have provided <Map<String, String>, String, Consumer<String>> information in the generic BiFunction. Compiler is able to refer the type information from there.
So, how are we going to use this curried map. Let us see that code.
static void consumeAList( final List<String> listOfString, final Consumer<String> mapUpdate){ listOfString .stream() .forEach(mapUpdate::accept); }
So, I created a consumeAList function which takes a list and a consumer as an argument. I stream the contents of that list and the provided consumer consumes the content.
That’s it , this is how we do it. We do not need to pass the map. Because, that map is already there in the lambda due to currying.
You can read the complete code on github, for better reading experience.
I have also put the same code over here.
import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.List; import java.util.Map; import java.util.HashMap; public class CurryAMap{ //Currying lambda I private final static BiFunction<Map<String, String>, String, Consumer<String>> curryAMap = (Map<String, String> map, String defaultValue) -> ((Consumer<String>) (String columnValue) -> map.put(columnValue, defaultValue)); private final static BiFunction<Map<String, String>, String, Consumer<String>> curryAMapSmaller= (map, defaultValue) -> (columnValue) -> map.put(columnValue, defaultValue); public static void main(final String ...args){ final List<String> listOfColumnNames = List.of("FirstName", "LastName", "FatherName"); final Map<String, String> mapOfColumnAndValue = new HashMap<>(); //Regular Way to do it. final String defaultValue = "NA"; for(final String columnName : listOfColumnNames){ mapOfColumnAndValue.put(columnName, defaultValue); } System.out.println("map for-loop " + mapOfColumnAndValue); putDefaultValue(mapOfColumnAndValue, listOfColumnNames, defaultValue); System.out.println("map func-call " + mapOfColumnAndValue); //Curried object final Consumer<String> mapUpdate = curryAMapSmaller.apply(mapOfColumnAndValue, "FromLamda"); consumeAList(listOfColumnNames, mapUpdate); System.out.println("map from lambda " + mapOfColumnAndValue); } static void putDefaultValue(final Map<String, String> map, final List<String> listOfColumns, final String defaultValue){ for(final String columnName : listOfColumns){ map.put(columnName, defaultValue); } } static void consumeAList( final List<String> listOfString, final Consumer<String> mapUpdate){ listOfString .stream() .forEach(mapUpdate::accept); } }