Swift Functional Programming: Basic Concepts
Kinda like reverse Fight Club for developers
Usually people that learn functional programming (also known as FP) don’t shut up about it, to a point where it would almost be irritable if it just wasn’t so darn cool. Imagine the famous of the 1999 classic cult movie Fight Club, but completely flipped on its head.
Rules of Functional Programming Club:
1. You do not shut up about Functional Programming.
2. You do NOT shut up about Functional Programming!
Some people also liken functional programmers to Crossfit enthusiasts, but unlike Crossfit, functional programming doesn’t have a 73.5% chance of you sustaining an injury preventing you from working. It actually improves the safety, readability and general awesomeness of the code you write.
Swift is by no means a purely functional programming language such as Haskell, but instead it’s more of a beautiful Frankenstein/multi-paradigm language by taking bits and pieces of other languages and making them cohesive with one another.
In this first part, we’re going to look at the simpler concepts of functional programming and demonstrate how the Swift language enables us to write functional code.
Basic Concepts
Immutability
let foo = 1
The easiest to understand, immutability simply means that once a value is set it cannot be changed, and we do this in Swift using the let
keyword when creating values.
The reason for immutability is because it allows us as developers to write thread-safe code. We can work with objects knowing with complete certainty that other threads within our application cannot change values as we are using them.
Value types
struct CGRect { }
When we pass around values instead of references to values, we create safer code. Structs are a great example of this too, pretty much everything inside the Swift standard library is a struct: Array
, Dictionary
, Int
, Bool
, and so forth, are all structs.
The reason why they’re safer than references is because when they’re passed around and used to set other objects, they’re copied on assignment which figuratively prevents rug from being pulled out from underneath us. Let me help you understand this concept with some code:
var box = CGRect.zero
var square = box.sizebox.size.height = 10// square: width: 0, height: 0
// box.size: width: 0, height: 10
If CGRect
and CGSize
were reference types instead of value types, square
’s height would have also changed to 10 when we changed box
’s height.
box.size.height = 10// square: width: 0, height: 10
// box.size: width: 0, height: 10
Pure functions
func sum(_ a: Int, _ b: Int) -> Int {
return a + b
}
A function where its return value is only determined by its input value/s, without any observable side-effects. It does one thing, and only one thing which is compute its return value, nothing else.
Even if we place a log inside the sum()
function, that would make it an impure function, which very contagious when writing code.
So basically the idea of writing pure functions is to eliminate all possibilities of side-effects which can increase the chance for bugs, and yes, this includes logging too.
First-class functions
func sayHello() {
print("Hi!")
}let greeting = sayHellogreeting()// prints: Hi!
In its creation, the Swift overlord creators decided in all their might to make almost everything a first-class citizen. When functions are said to be first-class, it means that we can assign functions to variables, just like we would with, say, an Int
or String
.
This allows us to write functions that can take other functions as arguments as well as return them, so that we may pass them around to other parts of our code.
Higher-order functions
Because functions are considered first class, that means we’re able to create “higher-order” functions. For a function to be considered higher-order, it must abide by at least one of the following two traits:
- Use a function as an argument
- Return a function
To illustrate an example of this, let’s create a higher-order function that accepts another function as an argument:
func inside() -> Void {
print("Yo!")
}// inside's structure:
// () -> Void
We have a function named inside
that takes no arguments and returns nothing, otherwise known as Void
. In the comment underneath it, I’ve described the function’s structure, which is very important to understand. The structure is what the compiler will analyse to validate compatibility when passing it into other functions as arguments. Now let’s see a function that accepts another function as an argument:
func outside(inner: () -> Void) {
inner()
}
As you can see, our outside
function takes one argument, which is also another function. If you look at inner
argument’s type, you can see it has the same structure as our inside
function. Because both the inside
function and the inner
argument’s parameters are the same, we are able to pass inside
into inner
and the compiler won’t say a peep or burst into flames.
Finally, we call the inner
parameter from within the outside
function and it will in turn call the print()
function that was in inside
’s body:
outside(inside)
// prints: Yo!
Advanced Concepts
Chaining, Composition and Currying
We’ve only just begun scratching the surface of functional programming in Swift, there is still a lot to cover with the next few parts of this ongoing series of posts, of which will include some more advanced concepts such as the three C’s above. They’re a bit complex so each will probably get its own post, but hopefully by now you have an improved understanding of how Swift incorporates the basic parts of the functional programming paradigm to make your code more robust, safer and versatile.
In saying that, I would like to formally welcome you to the Functional Programming Club. Please remember to practice Rules 1 & 2 as you gleefully await the next part of this series.
of this post can be found on GitHub.
If you like what you’ve read today you can check our my other articles or want to get in touch, please send me a tweet or follow me on , it really makes my day. I also organise in Melbourne, Australia and would to see you at the next event.