Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Once More, with Types

Rob Napier
November 15, 2016

Once More, with Types

Functional programming in Swift is all the rage, but many of the tools of functional languages don't translate easily to Swift. So much focus on lambdas and monads has caused us to overlook one of the most powerful lessons from decades of functional programming research: the power of strong types.

In this session we'll discuss what makes types weak or strong and how to build simple types that make whole classes of bugs impossible. We'll learn to tear over-complicated types apart and put them back together using simple rules. And we'll learn the basic vocabulary of types so we can talk about why to choose a struct or an enum, when to use an Optional, and how to fix common type mistakes in Swift.

Rob Napier

November 15, 2016
Tweet

More Decks by Rob Napier

Other Decks in Programming

Transcript

  1. 2

  2. 3 struct Talk { let name = "DO iOS" let

    timeInterval: TimeInterval = 1479204000 var date: Date { return Date(timeIntervalSince1970: timeInterval) } } let name: String = "DO iOS" let timeInterval: TimeInterval = 1479204000
  3. 4

  4. 8

  5. 9 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) ->

    Int { if loggedIn { return values.count } else { return 1 } } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if loggedIn { return normalCell(. . .) } else { return pleaseLoginCell(. . .) } } UITableViewController
  6. 10 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) ->

    Int { if loggedIn { if searching { return searchResults.count } else { return values.count } } else { return 1 } } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if loggedIn { if searching { return normalCell(...but with search results...) } else { return normalCell(. . .) } } else { return pleaseLoginCell(. . .) } } UITableViewController
  7. 11

  8. 12

  9. 14 func configureDataSource() { if loggedIn { if searching {

    dataSource = SearchingDataSource(. . .) } else { dataSource = BrowsingDataSource(. . .) } } else { dataSource = LoginDataSource(. . .) } tableView.dataSource = dataSource }
  10. 18 class ArrayDataSource<Element>: NSObject, UITableViewDataSource { let array: [Element] let

    identifier: String let configureCell: (UITableViewCell, Element) -> Void init(array: [Element], identifier: String, configureCell: @escaping (UITableViewCell, Element) -> Void) { self.array = array self.identifier = identifier self.configureCell = configureCell } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return array.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) configureCell(cell, array[indexPath.row]) return cell } }
  11. 19

  12. 20 var names = ["Bob", "Charlie", "Alice"] var ages =

    [43, 22, 34] How do I sort names according to ages? Alice Bob Charlie 34 43 22
  13. 21 How do I sort names according to ages? Easy!

    func unzip<Element1, Element2>(_ zipped: [(Element1, Element2)]) -> ([Element1], [Element2]) { var xs: [Element1] = [] var ys: [Element2] = [] for (x, y) in zipped { xs.append(x) ys.append(y) } return (xs, ys) } (names, ages) = unzip(zip(names, ages) .sorted { $0.0 < $1.0 }) var names = ["Bob", "Charlie", "Alice"] var ages = [43, 22, 34]
  14. 22 Oops, forgot to update this code when I added

    a new array. Why is ages not the same length as names? How’d that happen? $0.1, $0.2, $1.3 … which one is the name again? displayPerson(names[i], ages[i], userids[i], addresses[i], managers[i], datesOfHire[i], state[1], . . .) extension Array { mutating func sort<T: Comparable>(byOther: [T]){ self = unzip(zip(self, byOther) .sorted { $0.1 < $1.1 }) .0 } } Easy? names.sort ages.sort(byOther: names) userids.sort(byOther: names) addresses.sort(byOther: names) managers.sort(byOther: names) . . .
  15. 23

  16. 24

  17. 25 struct Person { let name: String let age: Int

    } var persons = [Person(name: "Alice", age: 43), Person(name: "Bob", age: 34), Person(name: "Charlie", age: 22), ] persons.sort { $0.age < $1.age } Simple!
  18. 26

  19. 28

  20. 30 Every value this 
 type can hold Every value

    this
 type can hold
 in this program
  21. 31

  22. 32 Invariants Arrays have same number of elements Arrays in

    a consistent order var names = ["Bob", "Charlie", "Alice"] var ages = [43, 22, 34] var persons = [Person(name: "Alice", age: 43), Person(name: "Bob", age: 34), Person(name: "Charlie", age: 22), ] Test all possible code paths to ensure invariants are true. Impossible to write code such that invariants wouldn’t be true.
  23. 35

  24. 41 Dictionary var book: [String: String] = [:]
 book["name"] =

    "Me"
 book["title"] = "Champion of Types" struct Book {
 var name: String
 var title: String
 }
  25. 42 Stringly Typed switch kind { case "start": print("Start") case

    "end": print("Done") default: fatalError("This shouldn't happen") } enum Kind { case start case end }
  26. 44 Int struct Person { struct ID { let value:

    Int } let id: ID let name: String }
  27. 48

  28. 52 struct Person { let name: String let address: String

    } (Person?, Error?) set nil set ⁇ ✅ nil ✅ ⁇ person error (String?, String?, Error?)
  29. 56

  30. 57