Higher-order functions in Swift; A term you’ve probably heard of, maybe you’ve used some of them or seen them used, but do you really know what a higher-order function is?

This article is devoted to explaining what a higher-order function is and how it differs from ordinary functions. Here, you will not only find the answer, but also see how to implement your own custom higher-order functions. Subsequent articles will demonstrate various common and well-known built-in higher-order functions in Swift.

There is only one idea to understand when it comes to higher-order functions. They are functions that take other functions as arguments, or they return a function as a result. Closures are the same; After all, a closure is a nameless function. So, once again…

Higher-order functions are functions that take other functions or closures as arguments, or return a function or closure as their result.

That sounds good, but what does it really mean in practice? To try it out, open up a Playground in Xcode and follow along. Whether or not it is possible to create your own higher-order functions, the following examples will help you fully understand what they are.

First, suppose we have the following enumeration representing the four basic mathematical operations:

enum  MathOperation  {

    case  addition,  subtraction,  multiplication,  division

}
Copy the code

Now, suppose we want to implement a function that does only one thing:

Based on the value we will supply it with MathOperation, it will return a function that will perform the actual math between the two operands.

For simplicity, we’ve agreed to deal only with Double values. This function is defined as follows:

func math(operation: MathOperation) -> (Double, Double) -> Double? {}Copy the code

View the return value; (Double, Double) -> Double? It is a declaration of a function without a name. We declare closures in exactly the same way, don’t we? The argument value matches two operands of type Double, and the result is also a Double. It marked? Symbol is optional, so the program does not crash if the division is zero.

Now let’s add something to the above functions, starting with the implementation of the four functions that will calculate the result of each operation. Note that all new functions are defined inside the Math (operation:) function – yes, we can do that – :


func  math(operation:  MathOperation)  ->  (Double,  Double)  ->  Double?  {

    func  addition(_  val1:  Double,  _  val2:  Double)  ->  Double  {  val1  +  val2  }

    func  subtraction(_  val1:  Double,  _  val2:  Double)  ->  Double  {  val1  -  val2  }

    func  multiplication(_  val1:  Double,  _  val2:  Double)  ->  Double  {  val1  *  val2  }

    func  division(_  val1:  Double,  _  val2:  Double)  ->  Double?  {val2  !=  0  ?  val1/val2  :  nil}

Copy the code

Next, we’ll return the correct function from math(operation:) based on the operation parameter value:

func  math(operation:  MathOperation)  ->  (Double,  Double)  ->  Double?  {

    func  addition(_  val1:  Double,  _  val2:  Double)  ->  Double  {  val1  +  val2  }

    func  subtraction(_  val1:  Double,  _  val2:  Double)  ->  Double  {  val1  -  val2  }

    func  multiplication(_  val1:  Double,  _  val2:  Double)  ->  Double  {  val1  *  val2  }

    func  division(_  val1:  Double,  _  val2:  Double)  ->  Double?  {val2  !=  0  ?  val1/val2  :  nil}

    switch  operation  {

        case  .addition:  return  addition(_:_:)

        case  .subtraction:  return  subtraction(_:_:)

        case  .multiplication:  return  multiplication(_:_:)

        case  .division:  return  division(_:_:)

    }

}

Copy the code

That’s a higher-order function! It returns another function as a result.

We can now use it as follows:

Let num2: Double = 5.5 let num2: Double = 10.5 var operation = math(operation:.addition) print(operation(num1, num2)! operation = math(operation: .multiplication) print(operation(num1, num2)!)Copy the code

The first time math(operation:) is called, it returns the addition(_:_:) function. The second time it returns a multi (_:_:) function, specified by the parameter provided. After performing the actual math between two given numbers, the print command displays the values 16.0 and 57.75, respectively.

Now let’s implement a function that takes another function as an argument, rather than returning a function as its result. I’ll start with four identical mathematical functions that perform the basic calculation:

func  addition(_  val1:  Double,  _  val2:  Double)  ->  Double  {  val1  +  val2  }

func  subtraction(_  val1:  Double,  _  val2:  Double)  ->  Double  {  val1  -  val2  }

func  multiplication(_  val1:  Double,  _  val2:  Double)  ->  Double  {  val1  *  val2  }

func  division(_  val1:  Double,  _  val2:  Double)  ->  Double?  {  val2  !=  0  ?  val1/val2  :  nil  }
Copy the code

With the authorization above, here is the definition of the new function:

func performOperation(val1: Double, val2: Double, operation: (Double, Double) -> Double) -> Double? {}Copy the code

The first two parameter values are the two operands that will be used to compute the result. The third parameter value, and the most interesting part here, is a function; It takes two arguments of type Double and returns an optional Double result.

In its body, there is only one line that calls the function that passes the first two parameter values as arguments:

func  performOperation(val1:  Double,

                      val2:  Double,

                      operation:  (Double,  Double)  ->  Double)  ->  Double?  {

    operation(val1,  val2)

}

Copy the code

The actual functions that operation will match depend on the functions we will provide as arguments. For example, to perform multiplication, we can call the above function with the two values we want to multiply, plus the function that performs multiplication:

var  result  =  performOperation(val1:  num1,  val2:  num2,  operation:  multiplication(_:_:))

print(result!)
Copy the code

To perform subtraction:

result  =  performOperation(val1:  num1,  val2:  num2,  operation:  subtraction(_:_:))

print(result!)
Copy the code

In the last two examples, we provided a named function as an argument. However, this is not mandatory. We can use closures instead. The following example demonstrates how to pass a closure as an argument to a demo higher-order function. In its body, we calculate and return the average of two given values:

result  =  performOperation(val1:  num1,  val2:  num2,  operation:  {  val1,  val2 in

    (num1  +  num2)/2

})

print(result!)
Copy the code

Now you know what the higher-order function terms mean and how they differ from ordinary functions called first-order functions. Although the examples provided are very simple, they are still sufficient to help you understand the topic.

In the next article, I’ll show how some of the built-in higher-order functions provided in Swift work and what you can do with them. I’ll start with the Map,compactMap, and flatMap functions, and then I’ll move on to some of the others. So stay tuned!