DEV Community

Paolo Sciarra
Paolo Sciarra

Posted on • Updated on

Currying vs Dependency Injection

In this article, I want to describe a very simple concept used in functional programming: currying.

We have to imagine that in most functional languages, we deal mainly with functions. I will use Java as language for my examples, but it should be fairly easy to understand them and adapt to your language of choice.

Let's analyse the type of few functions:

public interface ExampleFunctions {
 Integer invertSign(Integer input);
 Integer add(Integer a, Integer b);
 Command createFrom(Properties ps, Pattern p, CommandFactory f);
}
Enter fullscreen mode Exit fullscreen mode

Here we can see we have:

  1. invertSign that has type: Function<Integer, Integer>
  2. add that has type: Function<Integer, Integer, Integer>
  3. createFrom that has type: Function<Properties, Pattern, CommandFactory, Command>

For non Java programmers, the interface Function in Java is generic or parameterised and the first n parameters represent the function inputs or arguments, and the last is the return type.

Now let's focus our attention on the two functions that have more than one argument add and createFrom.

Suppose you want to create an adder object, that adds any integer to a given number, like a fiveAdder.

Using an object oriented style, you can write a class like:

public class Adder {
 private final Integer firstAddend;

 public Adder(Integer firstAddend){
  this.firstAddend = firstAddend;
 }

 public Integer add(Integer number){
   return firstAddend + number;
 }
}
Enter fullscreen mode Exit fullscreen mode

In this class we require a firstAddend integer dependency in the constructor, and then we have a different add method that takes just one parameter.

This way of passing parameters to an object is called Dependency Injection in this case through a constructor.

Using the concept of currying, we could have done this without declaring any class.

Currying is the transformation of a function that takes a structure of n arguments, into a chain of functions each taking one argument.

So for functions that take two arguments, we can write a curry function like this:

Function<A, Function<B, C>> curry(Function<A, B, C> f);
Enter fullscreen mode Exit fullscreen mode

Then using this function we could have written:

var fiveAdder = curry(ExampleFunctions::add).apply(5);
Enter fullscreen mode Exit fullscreen mode

Just like that, we have a function that adds any integer to five.

Now take for example the other function createFrom.

Imagine a situation where you have different Command objects, like TurnOnCommand and TurnOffCommand, and you want to use that method to create them in different ways (using different patterns, and different constructors), but for all of them, you want to use the same Properties object.

We can leverage the currying concept for functions with 3 arguments:

Function<A, Function<B, Function<C, D>>> curry(Function<A, B, C,D> f);
Enter fullscreen mode Exit fullscreen mode

And then write:

var someProperties = ...;
var factory = curry(ExampleFunctions::createFrom).apply(someProperties);
var turnOnFactory = factory.apply(turnOnPattern).apply(TurnOnCommand::new);
var turnOffFactory = factory.apply(turnOffPattern).apply(TurnOffCommand::new);
Enter fullscreen mode Exit fullscreen mode

There you have a factory function variable created after applying the first argument, someProperties, to the curried version of the createFrom method.

Without using currying you could have written:

var someProperties = ...;
var turnOnFactory = createFrom(someProperties, turnOnPattern, TurnOnCommand::new);
var turnOffFactory = createFrom(someProperties, turnOffPattern,TurnOffCommand::new);
Enter fullscreen mode Exit fullscreen mode

or using dependency injection:

public class CommandsFactory{
  private final Properties p;

  public CommandsFactory(Properties p){
    this.p = p;
  }

  public Command createFrom(Pattern p, CommandFactory f){
   ...
  }
}
Enter fullscreen mode Exit fullscreen mode

I hope you now have clear what currying means, and how you can use it as a replacement for new class creation and dependency injection, or how somehow these concepts are related.

Top comments (3)

Collapse
 
taqmuraz profile image
Taqmuraz

Nice article! Yes, function currying may replace almost every redundant syntax constructions in the modern programming languages. I wish Java to have more elegant function declaration and composition syntaxes.

Collapse
 
insouciantqualms profile image
InsouciantQualms

Well explained. I think showing the before/after in a language like Kotlin (or Scala) that has first-class functions would make the case even more strongly.

Collapse
 
itbhp profile image
Paolo Sciarra • Edited

Yes Java is a bit verbose, but compared to when I started with Java 1.2 things got better for sure :)