Function Composition-Swift
image source : google

Function Composition-Swift

Once you have a collection of small, re-usable functions in your code, you can create new functions combining small functions together to make bigger functions. This is called function composition, and I hope it will encourage you to ensure your functions can be composed easily! 

Swift does not have a compose operator – i.e., a special operator that makes function composition easy – so most people combine functions like this: 

let foo = functionC(functionB(functionA()))
        

Some functional programmers create a compose operator, >>>, that allows you to rewrite that code a little more clearly: 


let foo = functionC >>> functionB >>> functionA
        

This isn't hard to do using operator overloading. I'll show you the code then explain how it works: 


precedencegroup CompositionPrecedence {

    associativity: left

}

infix operator >>>: CompositionPrecedence
        

That declares a new operator, >>>, which uses three generic data types: T, U, and V. It works with two operands, lhs and rhs, which are two functions: lhs accepts a parameter of type T and returns a value of type U, and rhs accepts a parameter of type U and returns a value of type V. The whole >>> function returns a function that accepts a T and returns a V – it just merges the two steps by calling rhs(lhs($0)).

To make things a bit clearer, imagine our first function accepts an integer and returns a string, and the second function accepts a string and returns an array of strings. That would make >>> look like this: 


func >>> (lhs: @escaping (Int) -> String, rhs: @escaping (String) -> [String]) -> (Int) -> [String] {

    return  { rhs(lhs($0)) }

}
        

Hopefully that makes its behavior a bit clearer: by combining the two functions together, the resulting combined function accepts an integer (the input for the first function) and returns an array of strings (the output of the second function)/

This operator is just syntactic sugar, but let me give you an example that demonstrates how useful it is. Consider the following code: 


func generateRandomNumber(max: Int) -> Int {

    let number = Int(arc4random_uniform(UInt32(max)))

    print("Using number: \(number)")

    return number

}


func calculateFactors(number: Int) -> [Int] {

    return (1...number).filter { number % $0 == 0 }

}


func reduceToString(numbers: [Int]) -> String {

    return numbers.reduce("Factors: ") { $0 + String($1) + " " }

}
        

When called in a sequence, that will generate a random number, calculate its the factors, then convert that factor array into a single string. To call that in code you would normally write this: 


let result = reduceToString(numbers: calculateFactors(number: generateRandomNumber(max: 100)))

print(result)
        

That needs to be read from right to left: generateRandomNumber() is called first, then its return value is passed to calculateFactors(), then its return value is passed to reduceToString(). And if you need to write this code more than once, you need to make sure you keep the order correct even when making changes in the future.

Using our new compose operator, there's a better solution: we can create a new function that combines all three of those, then re-use that function however we need. For example: 


let combined = generateRandomNumber >>> calculateFactors >>> reduceToString

print(combined(100))
        

There are several reasons I love this approach. First, it's read naturally: generate a random number, calculate its factors, then reduce to a string. Second, it lets you save the combined function for use as many times as you need. Third, you can compose your functions even further: combined could be used with >>> to make an even bigger function. And fourth, the combined function automatically accepts the same parameter as the first function that was composed, so we use combined(100) rather than combined().

I hope this technique gives you another good reason to write small, modular functions: you can write as many as you want, each performing one small task, then create larger functions that combine those together in useful ways. 

To view or add a comment, sign in

More articles by Md Jubel Hossain

  • Building a Fully Dynamic, Type-Safe Core Data + CloudKit Stack in Swift

    As iOS developers, we all know the pain of managing Core Data. Every new project starts with repetitive boilerplate:…

  • Sorting complex data

    When you have an array that contains a custom data type, e.g.

  • Variadic functions — Swift

    Variadic function is a function which accepts a variable number of arguments. The function arguments are represented by…

  • Contiguous Array

    Swift provides two types of arrays, but almost always only one is used. You should be aware that the following two…

Explore content categories