Save 37% off PRO during our Black Friday Sale! »

Learning from our elders

Ed9a0d8cd44b62539b141f6c10405db1?s=47 Rob Napier
September 09, 2016

Learning from our elders

Swift is not a functional programming language. Pushing too hard to make it one fights Swift and breaks Cocoa.

But Swift has absorbed some fantastic lessons from the functional world, and while value types may not quite be the present, they are clearly the future. We’ll explore how decades of work in functional languages have influenced Swift, and how you can use those features best while staying true to Swift.

Ed9a0d8cd44b62539b141f6c10405db1?s=128

Rob Napier

September 09, 2016
Tweet

Transcript

  1. None
  2. None
  3. slice :: [a] -> Int -> Int -> [a] slice

    [] _ _ = [] slice xs i j = map snd . filter((>=i) . fst) $ zip [1..j] xs
  4. None
  5. Functional programming is a way of thinking.

  6. var persons: [Person] = [] for name in names {

    let person = Person(name: name) if person.isValid { persons.append(person) } }
  7. var persons: [Person] = [] for name in names {

    let person = Person(name: name) if person.isValid { persons.append(person) } }
  8. var persons: [Person] = [] for name in names {

    let person = Person(name: name) if person.isValid { persons.append(person) } }
  9. var persons: [Person] = [] for name in names {

    let person = Person(name: name) if person.isValid { persons.append(person) } }
  10. var persons: [Person] = [] for name in names {

    let person = Person(name: name) if person.isValid { persons.append(person) } }
  11. var possiblePersons: [Person] = [] for name in names {

    let person = Person(name: name) possiblePersons.append(person) } var persons: [Person] = [] for person in possiblePersons { if person.isValid { persons.append(person) } }
  12. var possiblePersons: [Person] = [] for name in names {

    let person = Person(name: name) possiblePersons.append(person) }
  13. extension Array where Element == String { func convertToPerson() ->

    [Person] { var possiblePersons: [Person] = [] for name in self { let person = Person(name: name) possiblePersons.append(person) } return possiblePersons } }
  14. extension Array where Element == String { func convertToPerson() ->

    [Person] { var possiblePersons: [Person] = [] for name in self { let person = Person(name: name) possiblePersons.append(person) } return possiblePersons } } let person = Person(name: name)
  15. extension Array where Element == String { func convertToPerson(with: (String)

    -> Person) -> [Person] { var possiblePersons: [Person] = [] for name in self { let person = with(name) possiblePersons.append(person) } return possiblePersons } }
  16. extension Array { func convert<T>(with: (Element) -> T) -> [T]

    { var possiblePersons: [T] = [] for name in self { let person = with(name) possiblePersons.append(person) } return possiblePersons } }
  17. extension Array { func convert<T>(with: (Element) -> T) -> [T]

    { var results: [T] = [] for element in self { let result = with(element) results.append(result) } return results } }
  18. let possiblePersons = names.convert(with: Person.init) var possiblePersons: [Person] = []

    for name in names { let person = Person(name: name) possiblePersons.append(person) }
  19. let possiblePersons = names.map(Person.init) var possiblePersons: [Person] = [] for

    name in names { let person = Person(name: name) possiblePersons.append(person) }
  20. public func map<T>( _ transform: (Iterator.Element) throws -> T )

    rethrows -> [T] { let count: Int = numericCast(self.count) if count == 0 { return [] } var result = ContiguousArray<T>() result.reserveCapacity(count) var i = self.startIndex for _ in 0..<count { result.append(try transform(self[i])) formIndex(after: &i) } _expectEnd(i, self) return Array(result) }
  21. var possiblePersons: [Person] = [] for name in names {

    let person = Person(name: name) possiblePersons.append(person) } var persons: [Person] = [] for person in possiblePersons { if person.isValid { persons.append(person) } }
  22. var persons: [Person] = [] for person in possiblePersons {

    if person.isValid { persons.append(person) } } let persons = possiblePersons.filter { $0.isValid }
  23. let possiblePersons = names.map(Person.init) let persons = possiblePersons.filter { $0.isValid

    } var persons: [Person] = [] for name in names { let person = Person(name: name) if person.isValid { persons.append(person) } }
  24. let possiblePersons = names.map(Person.init) let persons = possiblePersons.filter { $0.isValid

    }
  25. let persons = names .map(Person.init) .filter { $0.isValid }

  26. None
  27. None
  28. None
  29. None
  30. None
  31. UIViewController

  32. class DiscoverViewController: UIViewController,
 UITableViewDataSource,
 UITableViewDelegate, 
 UISearchResultsUpdating, 
 UISearchControllerDelegate, 


    UISearchBarDelegate, 
 DiscoverViewModelDelegate, 
 PresetDetailViewControllerDelegate {
  33. None
  34. 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(. . .) } }
  35. 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 the search results . . .) } else { return normalCell(. . .) } } else { return pleaseLoginCell(. . .) } }
  36. None
  37. None
  38. class BrowsingDataSource: NSObject, NSTableViewDataSource { class LoginDataSource: NSObject, NSTableViewDataSource {

    class SearchingDataSource: NSObject, NSTableViewDataSource {
  39. if bool { performX() } else {
 performY() } struct

    X: Performing { func perform() {} } struct Y: Performing { func perform() {} }
  40. func configureDataSource() { if loggedIn { if searching { dataSource

    = SearchingDataSource(. . .) } else { dataSource = BrowsingDataSource(. . .) } } else { dataSource = LoginDataSource(. . .) } tableView.dataSource = dataSource }
  41. None
  42. None
  43. class BrowsingDataSource: NSObject, NSTableViewDataSource { class SearchingDataSource: NSObject, NSTableViewDataSource {

    class LoginDataSource: NSObject, NSTableViewDataSource {
  44. class FetchRequestDataSource: NSObject, NSTableViewDataSource { class LoginDataSource: NSObject, NSTableViewDataSource {

  45. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView .dequeueReusableCell(withIdentifier: "Person") as! PersonCell let person = persons[indexPath.row] cell.textLabel?.text = person.name cell.imageView?.image = person.thumbnail . . . and more and more . . . return cell }
  46. None
  47. None
  48. None
  49. None
  50. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView .dequeueReusableCell(withIdentifier: "Person") as! PersonCell let person = persons[indexPath.row] cell.textLabel?.text = person.name cell.imageView?.image = person.thumbnail . . . and more and more . . . return cell }
  51. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView .dequeueReusableCell(withIdentifier: "Person") as! PersonCell cell.person = persons[indexPath.row] return cell }
  52. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView .dequeueReusableCell(withIdentifier: identifier) as! Cell cell.model = models[indexPath.row] return cell }
  53. class FetchRequestDataSource: NSObject, NSTableViewDataSource { class LoginDataSource: NSObject, NSTableViewDataSource {

  54. class StaticCellDataSource: NSObject, NSTableViewDataSource { class FetchRequestDataSource: NSObject, NSTableViewDataSource {

  55. None
  56. Wow! so precondition such ???optional?! ifelse much @testable

  57. None
  58. let sum = foldr (+) 0 sum [1..10] Haskell Create

    new function sum by combining
 existing functions foldr and +
  59. Swift Attach Sequence methods to MyStruct extension MyStruct<T>: Sequence {

    func makeIterator() -> AnyIterator<T> { return ... } }
  60. String ?

  61. Public domain, original by J. Howard Miller

  62. Every value this 
 type can hold

  63. Every value this 
 type can hold Every value this


    type can hold
 in this program
  64. Every value this 
 type can hold Every value this


    type can hold
 in this program
  65. None
  66. None
  67. with thanks to Tony Hoare

  68. None
  69. Any(Object)

  70. None
  71. ()

  72. Void

  73. public enum Never {}

  74. Dictionary

  75. var dict: [String: String] = [:]
 dict["name"] = "Me"
 dict["title"]

    = "Champion of Types" struct Person {
 var name: String
 var title: String
 }
  76. Stringly Typed

  77. switch kind { case "start": print("Start") case "end": print("Done") default:

    fatalError("This shouldn't happen") } enum Kind { case start case end }
  78. switch kind { case "start": print("Start") case "end": print("Done") default:

    fatalError("This shouldn't happen") } enum Kind: String { case start case end }
  79. Int

  80. struct Person { let id: Int let name: String }

  81. struct Person { struct ID { let value: Int }

    let id: ID let name: String }
  82. struct ID { let value: Int }

  83. None
  84. struct ImageCache { private var cache: [String: UIImage] = [:]

    }
  85. None
  86. None
  87. struct Array<T> func Array(t: Type) -> struct ≈

  88. func returnCount(array: Array) -> Int { return array.count }

  89. func returnCount<T>(array: Array<T>) -> Int { return array.count }

  90. struct Container { let array: Array func returnCount() -> Int

    { return array.count } }
  91. struct Container<T> { let array: Array<T> func returnCount() -> Int

    { return array.count } }
  92. None
  93. func login(username: String, password: String, completion: (String?, Error?) -> Void)

    login(username: "rob", password: "s3cret") { (token, error) in if let token = token { // success } else if let error = error { // failure } }
  94. func login(username: String, password: String, completion: (String?, Error?) -> Void)

    login(username: "rob", password: "s3cret") { (token, error) in if let token = token { // success } else if let error = error { // failure } }
  95. func login(username: String, password: String, completion: (_ token: String?, Error?)

    -> Void) login(username: "rob", password: "s3cret") { (token, error) in if let token = token { // success } else if let error = error { // failure } }
  96. struct Token { let string: String }

  97. func login(username: String, password: String, completion: (Token?, Error?) -> Void)

    login(username: "rob", password: "s3cret") { (token, error) in if let token = token { // success } else if let error = error { // failure } }
  98. func login(username: String, password: String, completion: (Token?, Error?) -> Void)

    login(username: "rob", password: "s3cret") { (token, error) in if let token = token { // success } else if let error = error { // failure } }
  99. struct Credential {
 var username: String var password: String }

  100. struct Style { let color: Color let isThick: Bool }

  101. func login(credential: Credential, completion: (Token?, Error?) -> Void) let credential

    = Credential(username: "rob", password: "s3cret") login(credential: credential) { (token, error) in if let token = token { // success } else if let error = error { // failure } }
  102. func login(credential: Credential, completion: (Token?, Error?) -> Void) let credential

    = Credential(username: "rob", password: "s3cret") login(credential: credential) { (token, error) in if let token = token { // success } else if let error = error { // failure } }
  103. set nil set ⁇ ✅ nil ✅ ⁇ token error

    let credential = Credential(username: "rob", password: "s3cret") login(credential: credential) { (token, error) in if let token = token { // success } else if let error = error { // failure } }
  104. enum Result<Value> { case success(Value) case failure(Error) }

  105. enum Style { case line case square(color: Color) }

  106. func login(credential: Credential, completion: (Result<Token>) -> Void) login(credential: credential) {

    result in switch result { case .success(let token): // success case .failure(let error): // failure } }
  107. → λ

  108. The Lessons • Break apart complicated things into simpler things

    • Look for generic patterns in the simple things • Lift and compose simple things to make complex ones
  109. None