π Function Composition in Swift
Itβs common to perform an operation on a value and use the resulting value of that operation as the argument for a subsequent operation, and so on.
// Definitions
func square(_ x: Int) -> Int {
return x * x
}
func increment(_ x: Int) -> Int {
return x + 1
}
func half(_ x: Int) -> Int {
return x / 2
}
func describe(_ val: CustomStringConvertible) -> String {
return "The resulting value is: \(val)"
}
// Usage
describe(increment(square(half(6)))) // "The resulting value is: 10"
This works fine but the code quickly starts looking like Racket, and becomes hard to unpack. Another way to do this same thing is by leveraging function composition, which entails chaining many functions together into one. This idea of concatenating functions can be accomplished in Swift by taking advantage of generics and the infix operator.
// Custom Operator Definition
precedencegroup Group { associativity: left }
infix operator >>>: Group
func >>> <A, B, C>(_ lhs: @escaping (A) -> B,
_ rhs: @escaping (B) -> C) -> (A) -> C {
return { rhs(lhs($0)) }
}
Here we have a function that takes 2 parameters and returns a closure.
- The first parameter in
>>>
is a function that takes 1 parameter of typeA
and returns an instance of typeB
. - The second parameter in
>>>
is a function that takes 1 parameter of typeB
(this is where the concatenation occurs) and returns an instance of typeC
. - Lastly,
>>>
returns yet another function which takes 1 parameter of typeA
and returns an instance of typeC
. TypeB
, being both the output for the 1st parameter and input for the 2nd one, serves as the knot betweenA
andC
. - The body of the function runs the result of
lhs
onrhs
and returns. These functions are nested internally so that the function parameters,lhs
andrhs
, can be described as a sequence externally.
We could then do the following:
// Usage With Custom Operator
(half >>> square >>> increment >>> describe)(6) // "The resulting value is: 10"
This then becomes a better and more legible approach of describing a sequence of operations, where functions are chained instead of being nested.
- Shout-out to Brandon Williams for showing me this stuff at Swift Summit 2017.
- If you find this interesting, feel free to download this playground. Also take a look at this Wikipedia article on Function Composition.