Functional Programming in Swift: From Closures to Composition
Swift was originally designed as a multi-paradigm language. It supports object-oriented, protocol-oriented, and functional programming. The functional approach helps write more predictable, testable, and concurrency-safe code. In this article, we will break down the key concepts of functional programming in Swift with practical examples.
1. Pure Functions and Immutability
The foundation of the functional style is pure functions. Such a function always returns the same result for the same arguments and has no side effects (does not change external state).
// Impure function — modifies an external variablevar total = 0func addToTotal(_ value: Int) -> Int { total += value return total}
// Pure function — only computationfunc add(_ a: Int, _ b: Int) -> Int { return a + b}Immutability is achieved through let and structures with value semantics. Instead of modifying an array, we create a new one:
let numbers = [1, 2, 3, 4, 5]// Instead of var and append:let newNumbers = numbers + [6] // [1, 2, 3, 4, 5, 6]2. Closures as Building Blocks
Closures in Swift are anonymous functions that can capture variables from their surrounding context. They are "first-class citizens": they can be passed as parameters and returned from functions.
// Simple closurelet multiply: (Int, Int) -> Int = { $0 * $1 }let result = multiply(3, 4) // 12
// Closure with context capturevar counter = 0let increment = { counter += 1 }increment() // counter = 1increment() // counter = 2Closures are actively used in higher-order methods such as sorted:
let names = ["Ivan", "Anna", "Boris", "Elena"]let sorted = names.sorted { $0.localizedStandardCompare($1) == .orderedAscending }print(sorted) // ["Anna", "Boris", "Elena", "Ivan"]3. Higher-Order Methods: map, filter, reduce
These are the "workhorses" of the functional style in Swift. They allow processing collections without explicit loops and mutation.
map
Transforms each element of the collection:
let prices = [100, 200, 300]let withTax = prices.map { $0 * 1.2 }print(withTax) // [120.0, 240.0, 360.0]
// Real example: extracting names from an array of modelsstruct User { let name: String let age: Int}let users = [User(name: "Alexey", age: 30), User(name: "Maria", age: 25)]let userNames = users.map { $0.name }print(userNames) // ["Alexey", "Maria"]filter
Selects elements based on a condition:
let numbers = [1, 2, 3, 4, 5, 6]let evenNumbers = numbers.filter { $0 % 2 == 0 }print(evenNumbers) // [2, 4, 6]
// Filtering users older than 18let adults = users.filter { $0.age >= 18 }reduce
Reduces the collection to a single value:
let sum = numbers.reduce(0, +) // 21// Building a string from an arraylet words = ["Functional", "programming", "Swift"]let sentence = words.reduce("") { $0.isEmpty ? $1 : $0 + " " + $1 }print(sentence) // "Functional programming Swift"Combining Methods
The most interesting part is method chaining:
let transactions = [250, -50, 100, -200, 300, -100]let balance = transactions .filter { $0 > 0 } // only income .map { $0 * 0.87 } // subtract 13% tax .reduce(0, +) // sum upprint(balance) // 565.54. Function Composition and Currying
Composition allows combining several functions into one. Swift does not have a built-in composition operator