What?
In this post I will talk about functional programming in Swift 2.0. Grab the latest release of Xcode 7 before trying out any of the following code because they might not compile on Swift 1.2.
For me, functional programming means a lot of things amongst which the followings stand out:
- Immutability (avoiding having variables with changing states, think let in Swift)
- Expectancy (knowing that a function does something, regardless of the environment under which it is running)
- Functions (that accept functions as their parameters, and can return functions)
Here is an example:
func doSoemthing(f: Int -> String) -> String -> Int{ return {(s: String) in Int(f(10))! } }
This is a function that takes in a function called f (f has an Int argument and returns a String). As a return value, doSomething returns a function, let’s called it z (z takes in a String, and returns an Int). If this is giving you headache, then I’m doing my job right.
map()
The map() function can be applied on collection types and sequence types (SequenceType). This function takes in a collection, and for every item in that array, calls a function and passes the items inside the collection to the function. That function can in turn, for every item in the collection, return anything.
Let’s say that you want to convert an array of integers to string. You can do it in the conventional way:
for i in 0..<10{ let s = String(i) print(s) }
Or you could do it in the functional way:
[0..<10].map{ String($0) }
Now, map can be used to convert anything, to anything. This is really powerful. It can take an array of functions, and convert them to an array of another type of functions. Here is an example:
typealias myFunc = Int -> String typealias yourFunc = String -> Int let f1: myFunc = { return String($0) } let f2: myFunc = { return String($0 + 100) } [f1, f2].map{(inputFunc: myFunc) -> yourFunc in let f: yourFunc = { Int(inputFunc(Int($0)!))! } return f }
This isn’t a common case but it is doable and it’s best to know about it. It’s best to know that anything can be mapped to anything, including functions to functions. Let’s look at another example. We have x number of functions that take a string and return a string. They do some operation on the incoming argument and return the results:
typealias strFunc = String -> String let toUpperCase: strFunc = { $0.uppercaseString } let appendZero: strFunc = { $0 + "0" }
I can now have a string and call these functions one by one on the string:
let str = "Vandad" print([toUpperCase, appendZero].map{ $0(str) })
I would then get this as the result printed to the console:
[VANDAD, Vandad0]
filter()
The filter function also works on sequence types and collections. This is a function that can be called on a collection and passes the items in that collection one by one to a function that we will write. Our function has to return true or false for every item in the collection, filtering it out basically. Let’s say that you want to find inside an array all integers that are larger than 5:
print(Array(0..<10).filter{ $0>5 })
Or let’s say that you want to find all even numbers inside an array:
Array(0..<10).filter{$0 % 2 == 0}
Note that you can also use stride to do this:
stride(from: 0, through: 10, by: 2)
reduce()
This function can be called on a collection. It takes in an initial value, and takes in a function as a parameter. Your function will then take in an item from the collection, and the initial value, and has to return the same data type as the initial value. Let’s have a look at an example.
Let’s say that you have an array of strings and an array of functions. These functions take in a string, do something with it, and return a string:
typealias strFunc = String -> String let toUpperCase: strFunc = { $0.uppercaseString } let appendZero: strFunc = { $0 + "0" } let prependZero: strFunc = { "0" + $0 }
So I want to call these 3 functions, to all my strings, and get the results. My strings are:
let strings = ["Vandad", "Aftonbladet"]
What I expect to get is an array with the first item equal to “0VANDAD0” and the next item as “0AFTONBLADET0”:
let result = [toUpperCase, appendZero, prependZero].reduce(strings) { (strs: [String], thisFunc: strFunc) -> [String] in strs.map(thisFunc) }
Thanks to Swift’s intelligent compiler, I can get rid of a lot of boilerplate code and rely on the compiler to find out the datatypes:
let result = [toUpperCase, appendZero, prependZero].reduce(strings) { $0.map($1) }
Final thoughts
Of course functional programming isn’t just about these 3 functions but these 3 are a good place to start.
/glhf