Swift – Currying Functions and Functions that return Functions
Currying allows you to turn a single function with multiple arguments into a series of functions – each with one argument. This allows you to store variables in functions and also create functions that return functions.
Functions that return functions
First of all lets look at a function that returns a function. The following code will return a function that allow’s us to show someones training progress in Swift.
func swiftProgress () -> ((String, Int) -> String) { func train(name: String, times: Int) -> String { return "\(name) has been taken \(times) Swift tutorials" } return train } let train = swiftProgress() train("Andrew", 3)
The output is as follows:
"Andrew has been taken 3 Swift tutorials"
The syntax is quite confusing so lets go over it line by line.
First of all
func swiftProgress () -> ((String, Int) -> String) {
This type of syntax is a function that returns a function. The function that is returned is indicated by ((String, Int) -> String). This means that the function that is return will accept a String and Int as arguments and return a String.
Next up is the train function:
func train(name: String, times: Int) -> String { return "\(name) has been taken \(times) Swift tutorials" } return train
This function accepts a name (String) and times (Int) as input and will output a String. We then return this train function to Swift Progress.
You will notice that this train function takes a String and Int as arguments and returns a String. See that this is the same format as ((String, Int) -> String). in the swiftProgress function – this means the train function get’s returned to the swiftProgress, and the input arguments and output must be the same!
So when we call swiftProgress as follows:
let swiftTrainer = swiftProgress()
What happens is the swiftProgress function will return a new function with is train. This is stored in the variable swiftTrainer. Then we can now call our swiftTrainer constant, it will actually be calling the train function, with a name and number of times they have trained in Swift.
swiftTrainer("Andrew", 3)
This will output:
Andrew has been taken 3 Swift tutorials
Pretty neat, lets take another example to see where this can come in useful
An ATM machine
In this example we have an ATM machine. For this ATM machine we only want one function to add and subtract money. We are going to create a function that will return a function that can either add or subtract money. Code this as follows:
func modifyMoney(subtract: Bool, amount: Int) -> (Int) -> Int { func addMoney(input: Int) -> Int { return input + amount } func subtractMoney(input: Int) -> Int { return input - amount } return subtract ? subtractMoney : addMoney } var atmMachine = 200 let add20 = modifyMoney(subtract: false, amount: 20) let subtract50 = modifyMoney(subtract: true, amount: 50) atmMachine = add20(atmMachine) // 220 atmMachine = subtract50(atmMachine) // 170
Let’s break it down again:
func modifyMoney(subtract: Bool, amount: Int) -> (Int) -> Int {
modifyMoney takes a subtract as a Bool which indicates if we are to subtract or add money to the account, and and accepts an Int indicating the amount. This will then return a function which takes in a Int and outputs an Int.
func addMoney(input: Int) -> Int { return input + amount } func subtractMoney(input: Int) -> Int { return input - amount }
These functions take an input, and will either add or subtract money from the account in Input, the amount will the the one we set in modifyMoney.
return subtract ? subtractMoney : addMoney
If we are subtracting money from the account then we return the subtractMoney function, otherwise we return the addMoney function. Remember that the func modifyMoney(subtract: Bool, amount: Int) -> (Int) -> Int, will return a function that accepts an Int and outputs an Int – in this case it will be either returning addMoney or subtractMoney.
var atmMachine = 200 let add20 = modifyMoney(subtract: false, amount: 20) let subtract50 = modifyMoney(subtract: true, amount: 50)
Now we declare an atmMachine with $200 in money in it. After this we declare two functions:
- add20 which will add $20 to the atmMachine
- subtract20 which will remove $50 from the atmMachine
atmMachine = add20(atmMachine) // atmMachine now has $220 atmMachine = subtract50(atmMachine) // atmMachine now has $170
We can then call these functions as above to add $20 and remove $50 from the atmMachine. We could extend this code further by adding validation only allowing certain amounts to be added and removed ($5, $10, $20, $50, $100). Anyhow this is an example as to a certain case where creating a function that returns a function comes in useful.
You may have notices with add20 the 20 is stored in the function, and subtract50 has the 50 stored in the function. This is a concept known as currying. We can call a function with will return a function that has some information stored in it, this is handy as you can create variables which have one function that is setup in several different ways as we have seen.
Currying functions
Now we know what currying is, lets make another function which takes advantage of this, lets make a function as follows which will multiply a number based on what we have stored in the function.
func multiplyBy(a: Int) -> (Int) -> Int { func nestedMultiply(b: Int) -> Int { return a * b } return nestedMultiply } let multiplyBy10 = multiplyBy(a: 10) let fifty = multiplyBy10(5)
As you can see multiplyBy10 will store the nestedMultiply function, with a set as 10. Now when we call this with multiplyBy10(5) it results in the output as 50. We can then call this multiplyBy10 function on any number to multiply it by 10, as 10 is stored in this function.
We can then call multiplyBy(a: Int) to reuse this with any number we want – pretty neat!
Conclusion
Functions that create functions, and function currying is a difficult concept to understand, it takes a few times of implementing it to start to see how it works. Essentially it allows you to:
- Create functions that can return different functions based on the input
- Reduce function arguments, and store some of the arguments within the function itself
The few examples outlined here should allow you to understand how it works, you can download the source code below. Be sure to subscribe for more tutorials. If you have any questions regarding this post leave them in the comments below.