Functional Programming in Swift: Advanced Level
Swift was originally designed as a multi-paradigm language, which makes it an ideal platform for applying advanced functional programming techniques. In this article, we will explore concepts that go beyond basic closures and map/filter/reduce — we will talk about functors, monads, function composition, currying, and memoization. These tools will help you write more predictable, testable, and expressive code for iOS and macOS applications.
Functors and Monads: Working with Contexts
A functor is a type that implements the map function, allowing you to apply a transformation to a value inside a context. In Swift, classic examples of functors are Optional and Array.
// Optional functorlet value: Int? = 42let result = value.map { $0 * 2 } // Optional(84)
// Array functorlet numbers = [1, 2, 3]let doubled = numbers.map { $0 * 2 } // [2, 4, 6]A monad is a functor with an additional flatMap operation (or >>= in Haskell). It allows "unwrapping" nested contexts. Swift supports monads through the Monad protocol implicitly, but key types (Optional, Result, Async) implement this logic.
// Optional monad: chaining operations with possible nilfunc parseNumber(_ string: String) -> Int? { return Int(string) }func half(_ number: Int) -> Int? { return number % 2 == 0 ? number / 2 : nil }
let input = "42"let result = input.flatMap(parseNumber).flatMap(half) // Optional(21)
// Result monad for error handlingenum AppError: Error { case invalidInput }func safeDivide(_ a: Int, _ b: Int) -> Result<Int, AppError> { guard b != 0 else { return .failure(.invalidInput) } return .success(a / b)}
let divisionResult = safeDivide(10, 2).flatMap { safeDivide($0, 1) } // .success(5)Advanced application: creating your own monadic type for working with asynchronous operations.
struct Async<T> { let run: (@escaping (T) -> Void) -> Void func map<U>(transform: @escaping (T) -> U) -> Async<U> { return Async<U> { callback in self.run { value in callback(transform(value)) } } } func flatMap<U>(transform: @escaping (T) -> Async<U>) -> Async<U> { return Async<U> { callback in self.run { value in transform(value).run(callback) } } }}Function Composition: Building a Pipeline
Function composition is creating a new function by sequentially applying several functions. In Swift, this can be implemented using an operator or closures.
// Composition operatorprecedencegroup CompositionPrecedence { associativity: left higherThan: FunctionArrowPrecedence}
infix operator >>> : CompositionPrecedence
func >>>(_ f: @escaping (A) -> B, _ g: @escaping (B) -> C) -> (A) -> C { return { x in g(f(x)) }}
// Example: validation and formattinglet trimWhitespace: (String) -> String = { $0.trimmingCharacters(in: .whitespaces) }let capitalize: (String) -> String = { $0.capitalized }let addExclamation: (String) -> String = { $0 + "!" }
let processString = trimWhitespace >>> capitalize >>> addExclamationprint(processString(" hello world ")) // "Hello World!"Composition is especially useful when working with chains of data transformations, for example, when processing network responses.
// Advanced composition with error handlingtypealias ResultFunction = (T) -> Result<U, Error>
func compose(_ f: @escaping ResultFunction<T, U>, _ g: @escaping ResultFunction<U, V>) -> ResultFunction<T, V> { return { input
```