Functional Programming

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Large software systems must be easy to maintain, test, and extend. Software developers use design principles and patterns to break large systems into modules with loose coupling. Concurrency introduces additional requirements and design approaches. Functional programming (FP) emphasizes immutability, with functions as the primary abstraction. You’ve seen how to apply object-oriented and protocol-oriented programming principles to your Swift apps. Swift also supports functional programming design, thanks to some fundamental language features.

Immutability & Stateless Programming

The first principle of functional programming is: Data is immutable. In an FP program, you create new data structures instead of modifying data in place. There are no unexpected state changes to crash your app, and concurrent programming is simplicity itself. If you could write Swift code where state never mutates, you’d never have to worry about data races or thread safety, and you’d never get bitten by hidden side effects.

Pure Functions

The second principle of functional programming is: Functions are first-class citizens. Like objects are the building blocks of an OOP app, FP programs are constructed with functions. Using an FP language, you can assign functions to variables, pass functions as arguments, and return functions from other functions. Higher-order functions take other functions as arguments or return them as results.

func ridesWithWaitTimeUnder(_ waitTime: Minutes,
                            from rides: [Ride]) -> [Ride] {
  return rides.filter { $0.waitTime < waitTime }
}
let shortWaitRides = ridesWithWaitTimeUnder(15, from: parkRides)

func testShortWaitRides(_ testFilter:(Minutes, [Ride]) -> [Ride]) {
  let limit = Minutes(15)
  let result = testFilter(limit, parkRides)
  print("rides with wait less than 15 minutes:\n\(result)")
  let names = result.map { $0.name }.sorted(by: <)
  let expected = ["Crazy Funhouse",
                  "Mountain Railroad"]
  assert(names == expected)
  print("✅ test rides with wait time under 15 = PASS\n-")
}

testShortWaitRides(ridesWithWaitTimeUnder(_:from:))

Declarative Programming Style

As you know from the higher-order functions you’ve used — map, filter, reduce — FP is a declarative programming style — the code states what you want to happen, not how. Here’s some simple imperative code, with its mutating array:

var newNumbers: [Int] = []
for number in numbers {
  newNumbers.append(number * number)
}
let newNumbers2 = numbers.map { $0 * $0 }
func squareOperation(value: Int) -> Int {
  print("Original Value is: \(value)")
  let newValue = value * value
  print("New Value is: \(newValue)")
  return newValue
}

let newNumbers3 = numbers.map(squareOperation(value:))

Function Composition

Another feature you get with FP higher-order functions is Function Composition: Because FP functions always have input and output values, you can compose functions by passing the output of one function as the input of another function.

func extractElements(_ content: String) -> [String] {
  return content.components(separatedBy: ",").map { String($0) }
}

func formatAsCurrency(content: [String]) -> [String] {
  return content.map {"$\($0)"}
}
let content = "10,20,30,40,50,60"
let elements = extractElements(content)
formatAsCurrency(content: elements)
formatAsCurrency(content: extractElements(content))
let formatExtractedElements = {
  data in
  formatAsCurrency(content: extractElements(data))
}

formatExtractedElements(content)
data in formatAsCurrency(content: extractElements(data))
precedencegroup ForwardPipe {
  associativity: left
}

infix operator |> : ForwardPipe

func |> <T, V>(
  f: @escaping (T) -> V,
  g: @escaping (V) -> V ) -> (T) -> V {
  return { x in g(f(x)) }
}
let extractThenFormat = extractElements |> formatAsCurrency
extractThenFormat(content)
See forum comments
Download course materials from Github
Previous: Functional Programming - Introduction Next: Functional Programming - Conclusion