Functional Currying

Balraj Singh - Feb 9 '23 - - Dev Community

So from our last session, we learned about Function Composition. Why do we need it? How to do it? How to make composition convenient by removing a lot of boilerplate? So continuing with the same theme of functional programming. The next stop will be at Function Currying.

So today we will try to answer following questions regarding Function Currying:-

  1. What is Function Currying?
  2. Why do we need function Currying?
  3. How to convert a function to curried function?
  4. How Function Currying help us in a day to day programming?

With these questions answered, we will be one step closer to using Function Composition more efficiently in a day to day programming.

So let's start now.

What is Function Currying?

Function Currying is a process of decomposing a function with multiple arguments to a sequence of functions with one argument. Currying helps us to apply value for some arguments and in return a partial function. Let's try to understand the concept with an example.
Let us see a simple Curried Function:-

// Normal way to define a function with 2 Arguments
func set<T>(key: String, data: T)

// Curried Function
func set<T>(key: String)(data: T)
Enter fullscreen mode Exit fullscreen mode

Why do we need function Currying?

We generally use function currying because of following advantages:-

  1. A curried function falls under the category of single argument function.

  2. On applying each value it generates a Partial Function which can be reused at multiple places

  3. Multiple argument functions are very hard to compose. Single argument functions can be composed easily

  4. Since it can be composed easily it gets to use all superpowers of Function Compositions

But there are a few disadvantages too:-

  1. In Swift by default, there are very few curried functions

  2. All Legacy code is not curried mostly

  3. Converting a non-curried to curried function is tedious and with a lot of boilerplate

How to convert a function to curried function?

Let’s take the above sample of set function with 2 arguments and see how to curry it.

// Simple set function
func set<T>(key: String, data: T) -> Void

// Curried wrapper for set function
func setCurried<T>(key: String)(data: T) -> Void {
 return { (key: String) -> (data: T) -> Void in { 
   (data: T) -> Void in
   // call your normal function
   set(key: key, data: data)
  }
 }
}
Enter fullscreen mode Exit fullscreen mode

Generalizing the above implementation for 2 arguments:-

/// Curries a function of two arguments.
///
/// — Parameter function: A function of two arguments.
/// — Returns: A curried function.
public func curry<A, B, C>(_ function: [@escaping](http://twitter.com/escaping) (A, B) -> C)
 -> (A)
 -> (B)
 -> C {
 return { (a: A) -> (B) -> C in
 { (b: B) -> C in
 function(a, b)
 }
 }
}

/// Using the above generalisation 
/// This yields a new function with following function signature
/// (String) -> (T) -> Void
curry(set) 
Enter fullscreen mode Exit fullscreen mode

As we have generalized over 2 arguments similarly we can generalize over N arguments. But due to Swift limitation of generalizing over N arguments is not possible. Hence we have implemented generalization till 12 arguments. This would cover 99% of the cases. Rarely do we create a function with more than 12 parameters. SwiftCurry

How Function Currying help us in a day to day programming?

Let us say we have created a caching framework with the following function signatures (Normal way to define a function with 2 Arguments):-

func set<T>(data: T, key: String) // Set Cache function definition
Enter fullscreen mode Exit fullscreen mode

Now when we use this function for setting user’s preference for app theme we will do it as follows

set(data: .dark, key: “AppThemeKey”)
Enter fullscreen mode Exit fullscreen mode

The above code looks perfectly fine. But it leaves many places to make errors. Few points are:-

  1. The key is a plain string hence while setting this value at multiple places can lead to mistakes.

  2. Key is not tied to its value. Hence one can make a mistake of saving the same value with a different key

Now let us try to solve the above issues.

// Convert the above set function to curried version
let curriedSet = curry(set)
Enter fullscreen mode Exit fullscreen mode

Let us generate a partial function to set App Theme

let setAppTheme = curriedSet(key: “AppThemeKey”)

/// To use it
setAppTheme(data: .dark)
Enter fullscreen mode Exit fullscreen mode

By creating the above partial function we have ensured that at no place developer needs to explicitly set the key hence solving the issue of making mistake with key String.
Also since we have defined the above partial function with a very particular objective to only set App Theme. Hence reducing the chances of making mistake in setting something else to expect App Theme.
We can make this more strict by introducing compile-time checks for a key-value pair but we will that for sometime later.

For now, I will leave you thinking about Curried & Practical Function. We will see more practical usage later.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .