Notes from Functional Swift
These are my notes from objc.io’s Functional Swift book. For a recent proejct, I’ve had to dive into a codebase that heavily makes use of functional programming in Swift; this book was helpful in getting a basic understanding of functional programming concepts in the world of Swift and iOS.
Characteristics of Functional Programming:
- Modularity
- Carteful treatment of mutable state
- Careful use of types
Functions are first-class values
- Can be passed as arguments to other funcitons
- Methods: functions defined on a type
- **Types guide the development process! **
- Higher-Order Functions - functions that take functions as arguments
Generics
- When functions can work on any type, use generics
- Type signature as
Map / Filter / Reduce
- Map - modify each element in a collection
- Transform one set of values into another set of values
- Filter - returns set of values that passed an
if
statement - Reduce - turns a collection into a single value
Any
vs Genertics
Generics can be used to define flexible functions
Any
used to dodge Swfit’s type systemOptionals represent values that may be missing or computations that may fail
?? checks if an optional argument is
nil
Optional binding: avoid writing “!”, use this instead:
if let x = yX { ... }
Optional chaining: calling methods / accessing properties on nested classes or structs
if let myThing = thing.one?.two?.three { ... }
guard
statement - exit current scope early if some condition isn’t met
Value and Reference Types
- Value types - copied when passes as function arguments (Structs)
- Reference types - references to the type are passed (Classes)
Structs
Examples of Structs
- Arrays
- Dictionaries
- Numbers
- Boolean
Structs allow for local mutability without global side effects
Coupling - measures the degree to which individual units of code depend on each other
- Fucntions that compute the same output for equal inputs = referentially transparent
- Favoring immutability makes it easier to write referentially transparent + reduces compuling!
Mutating Structs
extension Point Struct {
mutating func fooBar() {
x = 0
y = 1
}
}
Types
- Use types effectively to rule out invalid programs!
- Unlike Objective-C, enums in Swift create new types distinc from integers or other existing types
- Drawback to using Swift’s optional type: don’t return error when something goes wrong
- Swift forces you to annotate any function or method that may throw an error with the
trhows
error; forces you to usetry
and variants` - Enumerations - referred to as sum types
- Types defined using enumerations + structs are refered to as algebraic data types
Types are isomorphic if we can convert between them w/o losing any information:
f: (A) -> B
g: (B) -> A
- For all of x: A, the result of calling g(f(x)) must be equal to x
- For all of y: B, the result of f(g(y)) must equal Y
- These functions are the inverse of each other
DSL’s (Domain Specific Languages)
- Shallow embedding of DSL - does not create intermediate data structures
- Deep embedding - explicityl creates intermediate data structures
Iterators
Used if you don’t want to use all emenets in an array or calculate them all
Is a process that generates an array’s elements on request - adheres to this protocol:
protocol IteratorProtocol{ associatedType Element mutating func next() -> Element }
If we want to compute the indices in a different order, we only need to update the iteration, and never the code that uses it
separeate generation of data from usage
- By defining a protocol for iterators, we can also write generic methods that work for any iterator
- Iteraors can be combined on top of each other
Sequence
- Iterators provide a one-shot mechyanism for repeatedly computing a next element
- Sequences provide a mechanism for rewinding / replaying generated elements
- Every sequence has an associated iterator type + method to create a new iterator
- By encapsulating the creation of iterators in teh sequence definition, programmers uising sequences tdon’t have to to concerting with underlying iterators
- Map / reduce do not return new sequence, but traverse the sequence to produce an array
Lazy Sequences
- Chain operations only once we compute the result do operations get applied
Parser Combinators
- Functional approach to parsing
- Instead of managing mutable state of parser (e.g. what character we’re at), parser combinators are pure fu;nctions to avoid mutable state
- Parser - takes some chars as input, reaturns some resulting value + remainder of string if parsing succeeds
- Using
string
is bad for performance; usesubstring
instead
- Using