You are on page 1of 31

Introduction to

Functional Programming in Swift


Asfar
Functional Programming
Software development paradigm that puts
emphasis on
immutable state
pure functions
declarative programming.
Swift has excellent functional programming
support
apart from object-oriented.

2
Programming paradigm
Procedural languages
Cobol, Fortran, Basic, C
they describe, step by step, the procedure solve a
specific problem
Object-oriented programming
Smalltalk, C++, Obj-C, C#, Java, Swift.
Data and methods to manipulate it are kept as one
unit called an object.
The inner workings of an object may be changed
without affecting any code that uses the object.
Functional Programming
Haskell, Mercury, Clean - pure functional languages
Scala, Clojure, F#, - impure ones.

3
Functional Programming
Declarative programming
programming is done with expressions or
declarations instead of statements.
Pure functions (without side effects)
The output value of a function depends only on the
arguments that are passed to the function
so calling a function f twice with the same value for an
argument x produces the same result f(x) each time;
in contrast to procedures depending on a local or global
state.
No side effects, i.e., changes in state that do not
depend on the function inputs.

4
Immutable state
In a simple system, a mutable state is not problematic.
But in a large OOP system, a mutable state can produce many
headaches.
For instance, when two or more threads access the same
variable concurrently, they may modify or access it out of
order, leading to unexpected behavior.
This includes race conditions, dead locks and many other
problems.
Imagine if you could write code where the state never
mutated.
A whole issues that occur in concurrent systems would simply
vanish.
Can do this by creating an immutable property
data that is not allowed to change over the course of a program.

5
Functions as first-class objects
we can assign functions to variables,
pass them as function parameters,
return them from other functions and
do all other forms of data manipulation usually
reserved for objects.

func bar() {
print("foo")
}
let baz = bar
baz() // prints "foo"

See how we passed that function just like any other object
6
Functions in an array
func foo() { print("foo") }

func bar() { print("bar") }

func baz() { print("baz") }

let functions = [foo, bar, baz]

for function in functions {


function() // runs all functions
}
7
Anonymous functions (closures)
usually needed only in one place
The syntax for defining anonymous functions:

{(parameter1 : Type, parameter2: Type, ..., parameterN:


Type) -> ReturnType in

They can be defined like this:


let anonymous = { (item: Int) -> Bool in
return item > 2
}
8
Pure functions
Do not depend on anything besides their function parameters.
They do not change outside state, nor are affected by changes to
the outside state.

For example:
func countUp(currentCount: Int) -> Int {
return currentCount + 1
}
is a pure function.

var counter = 0
func countUp() -> Int{
counter += 1
return counter
}
is not a pure function.

9
Advantage of pure function
It makes the code extremely testable.
Can save a lot of time since they make debugging
easier.
Good for parallel programming since each new call
is depended only on the variables it created itself;
thus avoiding all of the parallel/concurrent programming
pitfalls
No synchronization issues.

10
Higher order functions
Functions which take other functions as
arguments and/or return other functions as a
result.
This simple concept lies at the core of
functional programming.
It enables us to create very useful abstractions
and deal with the specifics during the call to
the function
greatly reducing code doubling.

11
Example - remove all elements from a sequence/array that are
smaller than a certain number

An approach would be:

func filter(sequence: [Int], elementsSmallerThan border: Int) ->


[Int] {
var newSequence = [Int]()
for item in sequence {
if item < border {
newSequence.append(item)
}
}
return newSequence
}

filter(sequence: [1,2,3,4], elementsSmallerThan: 3) // returns [1,2]

12
Then later on another requirement - remove all the
elements that are larger than a certain number

func filter(sequence: [Int], elementsLargerThan border: Int) -> [Int]


{
var newSequence = [Int]()
for item in sequence {
if item > border {
newSequence.append(item)
}
}
return newSequence
}

filter(sequence: [1,2,3,4], elementsLargerThan: 2) // returns [3,4]

13
Now, we have a lot of code doubling.
Both times we need to go through a couple of
steps:
Create a new sequence
Iterate over items
Check the condition and append if its fulfilled
Return the sequence
So how could higher-order functions help us?

14
Create a single function which can take a condition as a
parameter and filter out a sequence of integers in any way
func filter(sequence: [Int], condition: (Int)->Bool) -> [Int] {
var newSequence = [Int]()
for item in sequence {
if condition(item) {
newSequence.append(item)
}
}
return newSequence
}

let sequence = [1,2,3,4]

let smaller = filter(sequence: sequence, condition: { item in item < 3 })

let larger = filter(sequence: sequence, condition: { item in item > 2 })

print(smaller) // prints [1,2]


print(larger) // prints [3,4]
Now we can filter out our sequence based on any condition we want!
We removed the boilerplate code and calls look more expressive than ever.

15
common higher-order functions in
Swift

filter
map
reduce.

16
filter
Use filter to loop over a collection and return an Array
containing only those elements that match an include
condition.
It has a single argument that specifies the include condition.
This is a closure(or function) that takes as an argument the
element from the collection and must return a Bool indicating if
the item should be included in the result.
filter applies
the input function to each element of the calling array
returns another array that has only the array elements for which
the parameter function returns true.

let sequence = [1,2,3,4]


let larger = sequence.filter {$0 > 2 })
let even = sequence.filter { $0 % 2 == 0 }

17
filter

18
map
Use map to loop over a collection and apply
the same operation to each element in the
collection.
The map function returns an array containing
the results of applying a mapping or transform
function to each item:
let values = [2.0,4.0,5.0,7.0]
let squares = values.map {$0 * $0}
// [4.0, 16.0, 25.0, 49.0]

19
map

20
syntax
Writing the mapping function in long form can make it easier
to see what is happening:
let values = [2.0,4.0,5.0,7.0]
let squares2 = values.map({ (value: Double) -> Double in
return value * value })
The closure has a single argument: (value: Double) and
returns a Double -> Swift can infer this.
map has a single argument which is a closure we do not need
the brackets - ( and )
with a single line closure we can even omit the return:
let squares2 = values.map {value in value * value}

The in keyword separates the argument from the body of the


closure.
If you prefer you can go one step further and use the
numbered arguments shorthand:
let squares = values.map { $0 * $0 }
21
map
The type of the results is not limited to the type of
the elements in the original array.
Here is an example of mapping an array of integers
to strings:

let scores = [0,28,124]


let words = scores.map {
NSNumberFormatter.localizedStringFromNumber($0,
numberStyle: .SpellOutStyle) }
// ["zero", "twenty-eight", "one hundred twenty-four"]

22
reduce
To combine all items in a collection to create a single value.
Takes two values, an initial value and a combine closure.
to add the values of an array to an initial value of 10.0:
let items = [2.0,4.0,5.0,7.0]
let total = items.reduce(10.0,combine: +) // 28.0

This will also work with strings using the + operator to


concatenate:
let codes = ["abc","def","ghi"]
let text = codes.reduce("", combine: +) // "abcdefghi"

The combine argument is a closure so you can also write


reduce using the trailing closure syntax:
let names = ["alan","brian","charlie"]
let csv = names.reduce("===") {text, name in "\(text),\(name)"}
// "===,alan,brian,charlie"
23
reduce

24
FlatMap
To flatten a collection of collections.
let collections = [[5,2,7],[4,8],[9,1,3]]
let flat = collections.flatMap { $0 } // [5, 2, 7, 4, 8, 9, 1, 3]

It knows about optionals and will remove them(nil) from a


collection.
let people: [String?] = ["Tom",nil,"Peter",nil,"Harry"]
let valid = people.flatMap {$0} // ["Tom", "Peter", "Harry"]

Can produce an Array which is the flattened concatenation of


transforming each of the subarrays.
To return an array of even integers contained in a collection of integer
arrays by applying a filter to each item in the subarrays:
let collections = [[5,2,7],[4,8],[9,1,3]]
let onlyEven = collections.flatMap { $0.filter { $0 % 2 == 0 } } // [2, 4, 8]
25
Chaining
You can chain methods.
For example to sum only those numbers greater than or equal
to seven we can first filter and then reduce:
let marks = [4,5,8,2,9,7]
let totalPass = marks.filter{$0 >= 7}.reduce(0,combine: +)
// 24

Another example that returns only the even squares by first


mapping and then filtering:
let numbers = [20,17,35,4,12]
let evenSquares = numbers.map{$0 * $0}.filter{$0 % 2 == 0}
// [400, 16, 144]

26
Advantages of FP
By taking a functional approach, code can be
more concise and clear.
Also, your code will be easier to test when
isolated into modular functions that are free
from side effects.
As multi-processing cores become the norm for
both CPUs and GPUs
minimizing side effects and issues from concurrency
will become increasingly important
FP will one of the most important ways to
achieve smooth performance!
27
Use it only when needed
Functional programming is a great way to
improve the readability and testability of your
code.
But remember - its not a one-size-fits-all
paradigm.
In fact, no paradigm is.
So use it when you need it
and combine it with other features in Swift to
make better software.

28
FP vs OOP
FP OOP
Fundamental unit function Object
of abstraction

Extensibility Higher order function inheritance and


(without existing polymorphism
code modification)
Code reuse parametric Inheritance or
polymorphism with composition
bounded
quantification using
Generic function

29
Mixing OOP and FP
OOP fits perfectly with FP when our objects are as
immutable as possible.
To make our objects as immutable as possible, we can
consider the following principles:
Objects should be types that encapsulate related pieces of data.
Objects can have methods; however, these methods shouldn't
change the object and should instead return a new one of the
appropriate type.
All the required state data should be injected into the class's
initialization so that it will be ready to use immediately.
Static methods can be used freely and static variables should be
avoided.
Protocols and generics should be used to avoid code duplicates.

30
OOP and FP can be used together.
Objects are closures with multiple methods, closures are
objects with a single method.
Swift supports OOP and FP.
Cocoa and Cocoa Touch frameworks that are OOP based
A great place to start working with FP techniques is
Model layer, ViewModel layer, and business logic.

For UI, how to use FP is not very clear.


An example of a FP-like approach for UI development
is Functional Reactive Programming (FRP)
becoming very popular
RxSwift is an reactive library for iOS and macOS
programming.
31

You might also like