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

Learning from our elders

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.

Rob Napier

September 09, 2016
Tweet

More Decks by Rob Napier

Other Decks in Programming

Transcript

  1. slice :: [a] -> Int -> Int -> [a] slice

    [] _ _ = [] slice xs i j = map snd . filter((>=i) . fst) $ zip [1..j] xs
  2. var persons: [Person] = [] for name in names {

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

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

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

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

    let person = Person(name: name) if person.isValid { persons.append(person) } }
  7. 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) } }
  8. var possiblePersons: [Person] = [] for name in names {

    let person = Person(name: name) possiblePersons.append(person) }
  9. 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 } }
  10. 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)
  11. 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 } }
  12. 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 } }
  13. 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 } }
  14. let possiblePersons = names.convert(with: Person.init) var possiblePersons: [Person] = []

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

    name in names { let person = Person(name: name) possiblePersons.append(person) }
  16. 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) }
  17. 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) } }
  18. var persons: [Person] = [] for person in possiblePersons {

    if person.isValid { persons.append(person) } } let persons = possiblePersons.filter { $0.isValid }
  19. 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) } }
  20. 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(. . .) } }
  21. 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(. . .) } }
  22. if bool { performX() } else {
 performY() } struct

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

    = SearchingDataSource(. . .) } else { dataSource = BrowsingDataSource(. . .) } } else { dataSource = LoginDataSource(. . .) } tableView.dataSource = dataSource }
  24. 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 }
  25. 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 }
  26. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

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

    let cell = tableView .dequeueReusableCell(withIdentifier: identifier) as! Cell cell.model = models[indexPath.row] return cell }
  28. let sum = foldr (+) 0 sum [1..10] Haskell Create

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

    func makeIterator() -> AnyIterator<T> { return ... } }
  30. ()

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

    = "Champion of Types" struct Person {
 var name: String
 var title: String
 }
  32. switch kind { case "start": print("Start") case "end": print("Done") default:

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

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

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

    let id: ID let name: String }
  36. 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 } }
  37. 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 } }
  38. 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 } }
  39. 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 } }
  40. 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 } }
  41. 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 } }
  42. 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 } }
  43. 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 } }
  44. 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 } }
  45. 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