A quick look at Swift functional programming

CocoaheadsSKG

Dimitri James Tsiflitzis

What is functional programming?

In computer science, functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids changing state, and mutable data.

Functional programming is a style of programming that explicitly uses functions that will always return the same result when called with the same input.

or

Filtering

The imperative way

var evens = [Int]()
for i in 1...10 {
  if i % 2 == 0 {
    evens.append(i)
  }
}
print(evens)

/*
[2, 4, 6, 8, 10]
*/

Functional filtering

In Swift, filter(_:) is a method on Collection types, such as Swift arrays.

It accepts another function as a parameter. This other function accepts as input a single value from the array, and returns a Bool.

Functional filtering

filter

true

true

Functional filtering

func isEven(number: Int) -> Bool {
  return number % 2 == 0
}

evens = Array(1...10).filter(isEven)
print(evens)

/*
[2, 4, 6, 8, 10]
*/

Functional filtering

/* functions in swift are just named closures */

evens = Array(1...10).filter { (number) in number % 2 == 0 }
        
print(evens)

/*
[2, 4, 6, 8, 10]
*/

Functional filtering

/* shorthand */

evens = Array(1...10).filter { $0 % 2 == 0 }

print(evens)

/*
[2, 4, 6, 8, 10]
*/

Functional filtering

Tip: Use the shorthand in simple situations when it will make sense at first glance to a future reader.

 

For more complicated scenarios you can use named arguments. Compilers have no feelings but humans do.

Don't give up on imperative programming just yet

func arrayFilter<T>(source: [T], predicate:(T) -> Bool) -> [T] {
  var result = [T]()
  for i in source {
    if predicate(i) {
      result.append(i)
    }
  }
  return result
}

Mapping

Functional mapping

The Collection method map(_:) accepts a single function as a parameter, and in turn, it produces an array of the same length after being applied to each element of the collection.

 

The return type of the mapped function does not have to be the same type as the collection elements.

 

Use map to loop over a collection and apply the same operation to each element in the collection.

Functional mapping

map

black

yellow

violet

ciel

f(black)

f(yellow)

f(violet)

f(ciel)

map

Functional mapping

typealias Years = Int

struct Person {
  let name : String
  let age  : Years
}


let persons = [
  Person(name: "John",   age: 40),
  Person(name: "Paul",   age: 74),
  Person(name: "George", age: 58),
  Person(name: "Ringo",  age: 76),
]

let personNames = persons.map { $0.name }
print(personNames)

/*
["John", "Paul", "George", "Ringo"]
*/

Imperative mapping

let values = [2.0,4.0,5.0,7.0]
var squares: [Double] = []
for value in values {
  squares.append(value*value)
}

print(squares)

/*
[4.0, 16.0, 25.0, 49.0]
*/

Functional mapping

let squares = [2.0,4.0,5.0,7.0]
        
func raiseSquare(number: Double) -> Double {
    return number * number
}
        
squares = squares.map(raiseSquare)
print(squares)

/*
[4.0, 16.0, 25.0, 49.0]
*/

Functional mapping

let values = [2.0,4.0,5.0,7.0]

let squares = values.map({
  (value: Double) -> Double in
  return value * value
})

print(squares)

/*
[4.0, 16.0, 25.0, 49.0]
*/

Functional mapping

let values  = [2.0,4.0,5.0,7.0]

let squares = values.map {value in value * value}

print(squares)

/*
[4.0, 16.0, 25.0, 49.0]
*/

Functional mapping

let values  = [2.0,4.0,5.0,7.0]

let squares = values.map { $0 * $0 }

print(squares)

/*
[4.0, 16.0, 25.0, 49.0]
*/

Reducing

Functional reducing

The Collection method reduce(_:_:) takes two parameters. The first is a starting value of a generic type Element, and the second is a function that combines a value of type Element with an element in the collection to produce another value of type Element.

 

Use reduce to combine all items in a collection to create a single new value.

Functional reducing

black

yellow

violet

ciel

reduce

Functional reducing

let items = [2.0,4.0,5.0,7.0]
let total = items.reduce(10.0,+)

print(total)

/*
 28.0
 */

Flat mapping​

Flat mapping

Use it to flatten a collection of collections.

let collections = [[5,2,7],nil, [4,8], nil, [9,1,3]]
let flat        = collections.flatMap { $0 }

/*
 [5, 2, 7, 4, 8, 9, 1, 3]
*/

Bonus: Swift ignores optionals for you

Flat mapping

Powerful when you transform each subcollection

let collections = [[5,2,7],[4,8],[9,1,3]]
let onlyEven    = collections.flatMap {
  intArray in intArray.filter { $0 % 2 == 0 }
}

/*
 [2, 4, 8]
 */

Chaining

Chaining

let marks        = [4, 5, 8, 2, 9, 7]
let passingTotal = marks.filter{$0 >= 7}.reduce(0,combine: +)

/*
 24
 */

let numbers     = [20, 17, 35, 4, 12]
let evenSquares = numbers.map{$0 * $0}.filter{$0 % 2 == 0}

/*
 [400, 16, 144]
 */

Thoughts

Swift is not a purely functional language but it allows you to be flexible and combine styles.


You can get started in your Model or ViewModel layer to get a feel for the lay of the land.


For your UI's checkout RxSwift. Reactive programming is an example of a functional programming, like, approach for UI development.

 

Your code will be easier to test when isolated into modular functions that are free from side effects. <~~ Famous Last Words

 

Ευχαριστούμε

A quick look at Swift functional programming

By tsif

A quick look at Swift functional programming

  • 212