Function Currying in Swift
Evangelist Apps
Experts in mobile app Development & Design for all iOS (iPhones, iPads, Apple Watches & Apple TV) and Android platforms
Introduction:
Function currying?is a technique in functional programming that transforms a function taking multiple arguments into a series of functions, each taking a single argument.
An American mathematician named Haskell Curry developed this technique, that’s why it is called currying.
(Currying does not refer in Indian curry ??)
Therefore, a function like f(a, b, c, …) can be converted into f(a)(b)(c)….
This process returns a new function for each argument until all arguments have been provided, and the original function can be evaluated. The resulting functions also called?curried functions, can be combined and composed to create more complex functionality.
Currying enables?partial function application, where some arguments are passed to a function, and a new function is returned, expecting the remaining arguments.
Example 1: Simple Add function
For example - consider the following function that takes two arguments:
func add(_ x: Int, _ y: Int) -> Int {
return x + y
}
add(2, 3) // output: 5
Above, we see a function with two arguments if we would like to change the numeric arguments we need to write a new function because:
We can curry this function in swift by returning a new function that takes one argument and returns another function that takes the second argument:
func curriedAdd(_ x: Int) -> (Int) -> Int {
return { y in
return x + y
}
}
// Now we can call this curried function like this:
print(curriedAdd(2)(3)) // 5
// we can also call it like this in 2 steps:
let addTwo = curriedAdd(2) // (Int) -> Int
print(addTwo(3)) // 5
// Add three numbers using curried function.
curriedAdd(curriedAdd(5)(2))(3) // 10
In this example:?curriedAdd(2)?returns a new function that expects one argument, which is then added to the original argument of 2 when called with?addTwo(3). We can also calculate the sum of?n?numbers by appending the resulting function with another curried function.
Example 2: Constructing URLs
In this example, we will create a list of URL strings using the ‘createURL’ function, which takes?baseURL?and?path?as arguments and returns a new URL string when called with these parameters.
func createURL(_ baseURL: String, _ path: String) -> String {
let scheme = "https";
return "\(scheme)://\(baseURL)/\(path)"
}
// create URLs for our main site
let homeURL = createURL("mysite.com", "");
let loginURL = createURL("mysite.com", "login");
let productsURL = createURL("mysite.com", "products");
let contactURL = createURL("mysite.com", "contact-us");
// create URLs for our careers site
let careersHomeURL = createURL("mysite-careers.com", "");
let careersLoginURL = createURL("mysite-careers.com", "login");
Notice how we are constantly repeating the baseURL argument to construct every URL. We can fix this by using function currying techniques.
func curriedCreateURL(_ baseURL: String) -> (String) -> String {
return { path in
let scheme = "https";
return "\(scheme)://\(baseURL)/\(path)"
}
}
// we create a new functions with the baseURL value returns another function.
let createSiteURL = curriedCreateURL("mysite.com");
let createCareersURL = curriedCreateURL("mysite-careers.com");
// create URLs for our main site
let homeURLCurried = createSiteURL("");
let loginURLCurried = createSiteURL("login");
let productsURLCurried = createSiteURL("products");
let contactURLCurried = createSiteURL("contact-us");
// create URLs for our career site
let careersHomeURLCurried = createCareersURL("");
let careersLoginURLCurried = createCareersURL("login");
We have created a new function with baseURL as a parameter, which returns a new function that takes path as a parameter and constructs a new URL.
Next, we created?partial function applications?such as?createSiteURL?and?createCareersURL, which we use to construct new URLs. By using function currying, we have eliminated the repetition of the base URL in function calls.
Example 3:?Isolate Expensive Process
Let’s say we have a large sample of data from a parcel company, containing a huge list of parcels from different countries along with their shipping addresses and the date which they were created.
var globalParcels: Parcels = [
Parcel(createdDate: 1,
location: "US",
shippingAddress: ....
),
Parcel(createdDate: 2,
location: "AUS",
shippingAddress: ....
),
Parcel(createdDate: 3,
location: "US",
shippingAddress: ....
)
// .........
]
To obtain a sorted list of all parcels for a given country based on their creation date, we can create a function that filters out the array by the specific country and sorts it by creation date. The function can be written as follows:
func sortParcelsByCountry(_ parcels: Parcels, _ country: String, _ sortOrder: String) -> Parcels {
let countryParcels = parcels.filter({ $0.location == country })
if sortOrder == "ascending" {
return countryParcels.sorted { $0.created > $1.created }
} else {
return countryParcels.sorted { $0.created < $1.created }
}
}
let ausParcelsAsc = sortParcelsByCountry(globalParcels, "AUS", "ascending")
let ausParcelsDsc = sortParcelsByCountry(globalParcels, "AUS", "descending")
The code above repeats parameters, which is a recurring issue. Furthermore, each call to ausParcelsAsc or asuParcelsDsc filters the large array of global parcel data, potentially causing an unnecessary performance bottleneck in the software. This could be particularly problematic if we need to re-sort the list multiple times. To address this issue, let’s utilize currying.
func sortParcels(_ parcels: Parcels) -> (String) -> (String) -> Parcels {
return { country in
let countryParcels = parcels.filter({ $0.location == country })
return { sortOrder in
if sortOrder == "ascending" {
return countryParcels.sorted { $0.created > $1.created }
} else {
return countryParcels.sorted { $0.created < $1.created }
}
}
}
}
let sortAusParcelsBy = sortParcels(globalParcels)("AUS");
let ausParcelsAsc1 = sortAusParcelsBy("ascending");
let ausParcelsDsc1 = sortAusParcelsBy("descending");
Observe that the costly filtering of global parcel data takes place just once during the creation of the partial application, sortAusParcelsBy. By executing sortAusParcelsBy, the function is able to access the previously filtered list within its closure scope. Consequently, we can avoid filtering our extensive parcel list multiple times when sorting it.
Conclusion:
Function currying is useful in functional programming because it allows for more flexible and reusable code. By breaking down a function into a sequence of smaller functions, we can compose them in various ways to create new functions with different behaviors.
Currying will make our code:
Hope you enjoyed reading this article.
Cheers!