Scala Tutorial

Basics

Control Statements

OOP Concepts

Parameterized - Type

Exceptions

Scala Annotation

Methods

String

Scala Packages

Scala Trait

Collections

Scala Options

Miscellaneous Topics

Higher Order Functions in Scala

Higher-order functions (HOFs) are a core concept in functional programming and are prominently featured in Scala. A higher-order function is a function that:

  • Takes one or more functions as arguments.
  • Returns a function as its result.

Here are some explanations and examples of higher-order functions in Scala:

1. Functions as Parameters:

A function can take another function as a parameter.

def applyTwice(f: Int => Int, x: Int): Int = f(f(x))

val result = applyTwice(_ * 2, 3) // Result: 12, because (3 * 2) * 2 = 12

In this example, applyTwice takes a function f and an integer x, then applies f to x twice.

2. Functions as Return Values:

A function can also return another function.

def multiplier(factor: Int): Int => Int = {
  x => x * factor
}

val double = multiplier(2) // 'double' is now a function that doubles its input
val result = double(5)     // Result: 10

Here, multiplier returns a function that multiplies its input by a given factor.

3. Anonymous Functions:

Often, when using higher-order functions, especially with collections, we use anonymous (or lambda) functions. These are functions without a name.

val numbers = List(1, 2, 3, 4, 5)
val doubled = numbers.map(x => x * 2)

Here, the map method is a higher-order function that takes a function as its argument.

4. Functional Combinators:

Scala collections provide many higher-order functions out of the box, often referred to as functional combinators. Some examples include:

  • map: Transform each element in the collection.
  • filter: Retain elements that satisfy a condition.
  • foldLeft / foldRight: Aggregate elements.
  • flatMap: Map and flatten the result.
val numbers = List(1, 2, 3, 4, 5)

val sum = numbers.foldLeft(0)((acc, x) => acc + x) // computes the sum

5. Function Composition:

In Scala, you can easily compose two functions to create a new function.

def f(x: Int): Int = x + 1
def g(x: Int): Int = x * 2

val h = f _ compose g // h(x) = f(g(x))
val result = h(3)    // Result: 7

6. Partially Applied Functions:

You can fix a number of arguments for a given function and produce a new function.

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

val add5 = add(5, _: Int)
val result = add5(3) // Result: 8

Here, add5 is a function that adds 5 to its argument.

Conclusion:

Higher-order functions are a powerful tool in Scala, allowing you to write expressive, concise, and clean code. They're especially useful in data transformation and processing tasks. Understanding and mastering them is key to effective functional programming in Scala.

  1. Scala functions as first-class citizens:

    • Description: Functions in Scala are first-class citizens, meaning they can be treated like any other value, assigned to variables, passed as arguments, and returned from other functions.
    • Code Example:
      val square: Int => Int = x => x * x
      val result = square(5)
      
  2. Higher-order functions vs. first-order functions in Scala:

    • Description: Higher-order functions take other functions as parameters or return them as results. First-order functions don't have this capability.
    • Code Example:
      // Higher-order function
      def applyTwice(f: Int => Int, x: Int): Int = f(f(x))
      
      // First-order function
      def square(x: Int): Int = x * x
      
      val result = applyTwice(square, 3)
      
  3. Function literals and anonymous functions in Scala:

    • Description: Function literals (anonymous functions) provide a concise way to define functions without explicitly giving them a name.
    • Code Example:
      val add: (Int, Int) => Int = (a, b) => a + b
      val result = add(3, 5)
      
  4. Passing functions as arguments in Scala:

    • Description: Functions can be passed as arguments to other functions, enabling powerful abstractions.
    • Code Example:
      def applyOperation(f: Int => Int, x: Int): Int = f(x)
      
      val result = applyOperation(x => x * x, 4)
      
  5. Returning functions from functions in Scala:

    • Description: Functions can be returned as results from other functions, allowing for flexible and modular code.
    • Code Example:
      def multiplier(factor: Int): Int => Int = x => x * factor
      
      val multiplyBy3 = multiplier(3)
      val result = multiplyBy3(7)
      
  6. Currying and partial application in Scala:

    • Description: Currying is the process of transforming a function with multiple arguments into a sequence of functions with one argument. Partial application is providing some, but not all, arguments to a function.
    • Code Example:
      def add(x: Int)(y: Int): Int = x + y
      
      val addFive = add(5) _ // Partial application
      val result = addFive(3)
      
  7. Higher-order functions with Scala collections:

    • Description: Scala collections provide higher-order functions like map, filter, and reduce for powerful and concise operations.
    • Code Example:
      val numbers = List(1, 2, 3, 4, 5)
      val squares = numbers.map(x => x * x)
      
  8. Map, filter, and reduce functions in Scala:

    • Description: map applies a function to each element, filter selects elements based on a predicate, and reduce combines elements into a single result.
    • Code Example:
      val numbers = List(1, 2, 3, 4, 5)
      val squares = numbers.map(x => x * x)
      val evens = numbers.filter(x => x % 2 == 0)
      val sum = numbers.reduce((x, y) => x + y)
      
  9. Composing functions in Scala:

    • Description: Function composition combines two functions to create a new function.
    • Code Example:
      val square: Int => Int = x => x * x
      val double: Int => Int = x => x * 2
      val squareAndDouble: Int => Int = square.compose(double)
      
      val result = squareAndDouble(3)
      
  10. Scala higher-order functions and immutability:

    • Description: Higher-order functions fit well with Scala's emphasis on immutability, allowing for expressive and safe code.
    • Code Example:
      val numbers = List(1, 2, 3, 4, 5)
      val doubledSquares = numbers.map(x => x * x).map(_ * 2)
      
  11. Function chaining and composition in Scala:

    • Description: Function chaining involves applying multiple functions sequentially. Function composition is a more formal way of combining functions.
    • Code Example:
      val addOne: Int => Int = _ + 1
      val square: Int => Int = x => x * x
      
      val result = addOne.andThen(square).apply(3)
      
  12. Recursion with higher-order functions in Scala:

    • Description: Recursion can be expressed using higher-order functions, like passing a function as an argument to itself.
    • Code Example:
      def factorial(n: Int): Int =
        if (n <= 1) 1
        else n * factorial(n - 1)