Scala - Currying Functions



This chapter takes you through the concept of currying functions in Scala programming. Currying is a technique that transforms a function with multiple arguments into a sequence of functions that each with a single argument.

Currying Functions

You can decompose a function that takes multiple parameters into a series of functions that each take a single parameter. It is used to create more modular and reusable code.

Currying is the process of transforming a function that takes multiple arguments into a chain of functions each taking a single argument.

Syntax

The syntax of a curried function in Scala is -

def functionName(arg1: Type1)(arg2: Type2)(arg3: Type3): ReturnType = {
   // function body
}

Example

The following example shows defining and using a curried function in Scala programming -

object Demo {
   def add(a: Int)(b: Int): Int = {
      a + b
   }

   def main(args: Array[String]): Unit = {
      println(add(3)(4))  // Output: 7
      val addThree = add(3) _
      println(addThree(4))  // Output: 7
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

7
7

In the example, the add function is curried. So it has to be called with one argument at a time.

Advantages of Currying Functions

There are various advantages of Currying functions. You can fix a few arguments of a function and produce a new function with less number of arguments. It is easier to build complex functions from simpler ones. You can also work with higher-order functions. So it enhances code modularity and reusability.

Partial Application with Currying

You can also use currying functions through partial application. You partially apply a function by fixing some of its arguments and returning a new function.

Syntax

The syntax for partially applying a curried function is -

def functionName(param1: Type1)(param2: Type2): ReturnType = {
   // function body
}

val partiallyAppliedFunction = functionName(fixedParam1) _

Example

Consider the example of partial application with curried functions in Scala programming -

object Demo {
   def multiply(a: Int)(b: Int): Int = {
      a * b
   }

   def main(args: Array[String]): Unit = {
      val multiplyByTwo = multiply(2) _
      println(multiplyByTwo(5)) 
      println(multiplyByTwo(10))
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

10
20

In the example, the multiply function is curried. It is partially applied to create a new function multiplyByTwo.

Currying and Function Composition

You can compose functions to pass partially applied functions as arguments to other functions.

Syntax

The syntax for composing functions with currying is -

def function1(param1: Type1)(param2: Type2): ReturnType1 = {
   // function1 body
}

def function2(param: Type3): ReturnType2 = {
   // function2 body
}

val composedFunction = function2 _ compose function1(fixedParam1)

Example

Consider the example of function composition with curried functions in Scala programming -

object Demo {
   def add(a: Int)(b: Int): Int = a + b

   def double(x: Int): Int = x * 2

   def main(args: Array[String]): Unit = {
      val addThree = add(3) _
      val addThreeAndDouble = double _ compose addThree
      println(addThreeAndDouble(4)) 
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

14

In the example, the add function is curried. It is composed with the double function to create a new function addThreeAndDouble.

Currying and Higher-Order Functions

You can use Currying combined with higher-order functions. So you can create highly modular and reusable code.

Syntax

The syntax for using curried functions with higher-order functions is -

def higherOrderFunction(f: Type1 => Type2): ReturnType = {
   // function body calling f
}

def curriedFunction(param1: Type1)(param2: Type2): ReturnType = {
   // function body
}

Example

Consider the example of using curried functions with higher-order functions in Scala programming -

object Demo {
   def applyFunction(f: Int => Int, x: Int): Int = {
      f(x)
   }

   def add(a: Int)(b: Int): Int = a + b

   def main(args: Array[String]): Unit = {
      val addFive = add(5) _
      println(applyFunction(addFive, 10)) 
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

15

In the example, the add function is curried. It passed as an argument to the higher-order function applyFunction.

Currying and Recursion

You can apply Currying in recursive functions to create elegant and concise solutions to complex problems.

Syntax

The syntax for recursive curried functions is -

def recursiveFunction(param1: Type1)(param2: Type2): ReturnType = {

   // base case
   if (condition) baseResult
   else {
      // recursive case
      recursiveFunction(newParam1)(newParam2)
   }
}

Example

Consider the example of recursive curried functions in Scala programming -

object Demo {
   def gcd(a: Int)(b: Int): Int = {
      if (b == 0) a
         else gcd(b)(a % b)
   }

   def main(args: Array[String]): Unit = {
      println(gcd(54)(24)) 
   }
}

Save the above program in Demo.scala. Use the following commands to compile and execute this program.

Command

> scalac Demo.scala
> scala Demo

Output

6

In the example, the gcd function is a recursive curried function. It computes the greatest common divisor of two numbers.

Currying Recursion Summary

  • Currying transforms a function with multiple arguments into a sequence of functions that each with a single argument.
  • You can use Currying in partial application, function composition, and with higher-order functions. So, it is easier to create new functions by fixing some arguments of existing functions.
  • The syntax for declaring curried functions can be different but the core concept remains the same.
  • It enhances code modularity and reusability because functions are broken down into simpler and more manageable parts.
  • It can be used in recursion and combined with other functions to solve complex problems elegantly.
Advertisements