Notes from Functional Swift

Published on Nov 26, 2022

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 system

  • Optionals 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 use try 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; use substring instead