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

Going functional in Swift

Going functional in Swift

Swift is so-called multi paradigm programming language, but iOS, on the other hand, is living in Object Oriented world and doesn't feel like going anywhere. From time to time we spot some functional stuff like map, filter and reduce, but are we actually using the on purpose, or is rather a coincidence? Where's the place for Protocol Oriented Programming? Let's learn something from functional programming and write better iOS applications.

#mobiconf2017

Mateusz Zając

October 06, 2017
Tweet

More Decks by Mateusz Zając

Other Decks in Programming

Transcript

  1. Going functional in (But only a bit) #mobiconf2017 MOBICONF.ORG |

    5-6 OCTOBER 2017
  2. Hi, I'm Mateusz! @cojoj #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  3. #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  4. Agenda: OOP + iOS + (FP + Swift) = #mobiconf2017

    MOBICONF.ORG | 5-6 OCTOBER 2017
  5. Functions Inheritance Algebraic Data Types Surprise! #mobiconf2017 MOBICONF.ORG | 5-6

    OCTOBER 2017
  6. Why Functional? · Functional Swi! by objc.io team · FRP

    frameworks (RxSwi!, ReactiveCocoa) · 3rd party libraries (Swi!z, Argo) · Tons of articles! #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  7. They all can't be wrong, right? #mobiconf2017 MOBICONF.ORG | 5-6

    OCTOBER 2017
  8. What's so special about FP, that iOS dev should take

    a look at it? #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  9. It can help you expand your brain with some common

    iOS pitfalls! #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  10. Ok, let's establish some common ground #mobiconf2017 MOBICONF.ORG | 5-6

    OCTOBER 2017
  11. Functional programming is a computer programming paradigm that relies on

    functions modeled on mathematical functions. — Haskell Programming from first principles #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  12. #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  13. OOP FP Single Responsibility principle Functions Open/Closed principle Functions Dependency

    Inversion principle Functions, also Interface Segregation principle Functions Factory pattern Yes, functions Strategy pattern Oh my, functions again! Decorator pattern Functions Visitor pattern Functions #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  14. However, it isn't about mathematics, but about abstraction and reducing

    complexity. #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  15. Functional programming qualities: 1. Modularity 2. A Careful Treatment of

    Mutable State 3. Types Taken from Functional Swi!
  16. Swift is a general-purpose programming language... #mobiconf2017 MOBICONF.ORG | 5-6

    OCTOBER 2017
  17. It's main target are platforms, though... #mobiconf2017 MOBICONF.ORG | 5-6

    OCTOBER 2017
  18. ...and those still carry the burden of Objective-C... #mobiconf2017 MOBICONF.ORG

    | 5-6 OCTOBER 2017
  19. ...which is object-oriented language! #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  20. The Objective-C language was chosen for a variety of reasons.

    First and foremost, it’s an object-oriented language. The kind of functionality that’s packaged in the Cocoa frameworks can only be delivered through object-oriented techniques. — , Object-Oriented Programming with Objective-C #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  21. Does it make Swift OOP language as well? ! #mobiconf2017

    MOBICONF.ORG | 5-6 OCTOBER 2017
  22. It kinda do... We're used to seeing it in OOP

    context, since iOS is in this context as well. #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  23. It doesn't mean we can't find any functional patterns in

    Swift! #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  24. (...) we need to write a bit of extra code

    to achieve the same results that languages like Haskell have with built-in operators. — Swi! Functors, Applicatives, and Monads in Pictures #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  25. It's not perfect, but consider job done... #mobiconf2017 MOBICONF.ORG |

    5-6 OCTOBER 2017
  26. But sometimes misuse overgrows the purpose... #mobiconf2017 MOBICONF.ORG | 5-6

    OCTOBER 2017
  27. We just need to find this misuse and start using

    it on purpose. #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  28. Where do we start? #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  29. Functions #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  30. func viewDidLoad() { super.viewDidLoad // Setup UI navigationItem.title = "Details"

    // Setup delegates, data sources etc. tableView.delegate = self tableView.dataSource = self // Fetch initial data networking.fetchPosts { [weak self] json in // Serialize JSON let posts = self.decodeJSON(Post.self, from: json) // Handle response and present it self.dataSource = posts self.tableView.reloadData() // Persist fetched data for post in posts { PersistanceManager.shared.save(post) } } // Set some user defaults UserDefaults.standard.set("UserHasSeenEULA", forKey: "Permissions") } #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  31. Higher-order functions #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  32. 1. Takes one or more functions as arguments 2. Returns

    a function as its result #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  33. Swift higher order functions: map flatMap filter reduce forEach first

    drop contains sorted #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  34. Their power lies in chaining (And being super generic) #mobiconf2017

    MOBICONF.ORG | 5-6 OCTOBER 2017
  35. // Adding additional step to this chain is super simple.

    // We don't have any nested loops, if statements etc. ["1", "2", "7", "Eight", "Nine", "10"] .flatMap(Int.init) .map { $0 * 3 } .filter { $0 > 5 } .reduce(0, +) #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  36. Actually, that's what FRP frameworks do! self.provider .request(.login(email: self.username.value, password:

    self.password.value)) .filterSuccessfulStatusCodes() .mapJSON() .withLatestFrom(self.shouldRemeberUser) { (_, shouldRemeber) in let username = self.username.value let password = self.password.value return (username, password, shouldRemeber) } .subscribe { (username, _, _) in print("Hi, \(username)") } .disposed(by: disposeBag) #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  37. But, be careful... #wayTooComplex #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  38. Pure functions #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  39. 1. The function always evaluates the same result value given

    the same argument value 2. Evaluation of the result does not cause any semantically observable side effect or output #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  40. Translated into Swiftish 1. If you need something inside the

    function, pass it with arguments 2. Don't write functions without return statement (if you don't have to) #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  41. // Sample of nice, predictable, pure function // Imagine how

    easy it's to write a test cases for it! func decode<T: Decodable>(for model: T.Type, from json: [String: Any]) throws -> T // This is a function depending on some external state, not returning anything. // To test it we'd need to mock the whole context... func decode() #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  42. Function Composition #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  43. An act or mechanism to combine simple functions to build

    more complicated ones. — Wikipedia #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  44. Sounds super simple, right? #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  45. // It's totally unreadable... removeInvalidRows(fromRows: createRows(fromLines: splitLines(ofText: csv))) https://github.com/ijoshsmith/function-composition-in-swi!

  46. It looks super UGLY! #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  47. // That's why we tend to do it this way

    let lines = splitLines(ofText: csv) let rows = createRows(fromLines: lines) let validRows = removeInvalidRows(fromRows: rows) https://github.com/ijoshsmith/function-composition-in-swi!
  48. -- Just to give you a grasp of how beautiful

    -- and simple it may look in Haskell splitLines . createRows . removeInvalidRows $ csv #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  49. // If we define a function like this one func

    ∘ <A, B, C> (aToB: @escaping (A) -> B, bToC: @escaping (B) -> C) -> (A) -> C { return { a in let b = aToB(a) let c = bToC(b) return c } } // We can get much better code let processCSV = splitLines() ∘ createRows() ∘ removeInvalidRows() let validRows = processCSV(csv) https://github.com/ijoshsmith/function-composition-in-swi!
  50. Inheritance #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  51. open class NSArray: NSObject, CKRecordValue, CustomReflectable, CVarArg, Equatable, ExpressibleByArrayLiteral, Hashable,

    NSCopying, NSFastEnumeration, NSMutableCopying, NSSecureCoding, Sequence #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  52. Type classes #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  53. Way of creating the type composition in the world without

    inheritance. — Krzysztof Siejkowski #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  54. -- This is just a declaration of typeclass class Eq

    a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y) #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  55. -- Just a sample data type data TrafficLight = Red

    | Yellow | Green -- Here we implement instance instance Eq TrafficLight where Red == Red = True Green == Green = True Yellow == Yellow = True _ == _ = False #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  56. Haskell's Typeclasses key to success is the amount of instances

    provided by basic libraries! #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  57. 227 instances implements Eq! #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  58. Protocols #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  59. protocol Eq { associatedtype T func equal(_ to: T) ->

    Bool } extension Eq { func notEqual(_ to: T) -> Bool { return !equal(to) } } extension Int: Eq { func equal(_ to: Int) -> Bool { return self == to } } 1.equal(2) // Prints false 1.notEqual(3) // Prints true #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  60. In Swift/CocoaTouch over 1000 elements implement Equatable! #mobiconf2017 MOBICONF.ORG |

    5-6 OCTOBER 2017
  61. Typeclasses give us more abstraction, precision and flexibility #mobiconf2017 MOBICONF.ORG

    | 5-6 OCTOBER 2017
  62. Next time you write some code, please think about this...

    #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  63. Do I need it only for X or should it

    work for all Ys as well? #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  64. Algebraic Data Types #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  65. URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in guard let

    data = data else { if let error = error { print(error.localizedDescription) } return } let decoder = JSONDecoder() let result = try! decoder.decode([String: String].self, from: data) }).resume() #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  66. Product Types Sum Types #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  67. Type that can be one of multiple possible options. Each

    option can carry a different type inside of it and, crucially, each option has a unique label. — Tikhon Jelvis #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  68. // Really simple sum types data Bool = False |

    True data Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 // Sum types with parameters data Maybe a = Just a | Nothing data Either a b = Left a | Right b #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  69. Enumerations #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  70. enum Optional<T> { case none case some(T) } #mobiconf2017 MOBICONF.ORG

    | 5-6 OCTOBER 2017
  71. Optionality can sometimes mean more than just an existence. —

    paraphrased Soroush Khanlou #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  72. func safeDiv(x: Float, y: Float) -> Float? { guard y

    != 0 else { return nil } return x / y } #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  73. How do we inform our user that he needs to

    take math classes again? #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  74. enum Either<T1, T2> { case left(T1) case right(T2) } func

    safeDiv(x: Float, y: Float) -> Either<String, Float> { guard y != 0 else { return .left("Error: division by zero") } return .right(x / y) } #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  75. Either can be used as a generalization of optional types

    in which Left not only encodes failure, but is accompanied by an error message — Marcello Seri #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  76. Sum types give us exhaustiveness and more certainty! #mobiconf2017 MOBICONF.ORG

    | 5-6 OCTOBER 2017
  77. Go and read "That One Optional Property" by Soroush Khanlou

    #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  78. Pssst, Sum Types play pretty well with pattern matching! #mobiconf2017

    MOBICONF.ORG | 5-6 OCTOBER 2017
  79. Last, but not least... #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  80. None
  81. Functors Applicative Functors Monads #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  82. ? is a Monad, period. #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER

    2017
  83. if let beginsWithThe = john.residence?.address?.buildingIdentifier()?.hasPrefix("The") { if beginsWithThe { print("John's

    building identifier begins with \"The\".") } else { print("John's building identifier does not begin with \"The\".") } } #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  84. You take a wrapped value, you apply a function to

    it and you pack it back in the wrapper! #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  85. // PromiseKit class Promise<T> { func then<U>(body: T -> U)

    -> Promise<U> func then<U>(body: T -> Promise<U>) -> Promise<U> } // RxSwift extension ObservableType { func map<R>(transform: E -> R) -> Observable<R> func flatMap<O: ObservableConvertibleType>(selector: E -> O) -> Observable<O.E> } // Interstellar extension Observable { func map<U>(transform: T -> U) -> Observable<U> func flatMap<U>(transform: T -> Observable<U>) -> Observable<U> } #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  86. What are they useful for? #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER

    2017
  87. ⛓ Chaining #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  88. But you've said the same thing about Higher Order Functions...

    #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  89. Because most types implementing them are Monads! #mobiconf2017 MOBICONF.ORG |

    5-6 OCTOBER 2017
  90. And that's the beauty of FP #mobiconf2017 MOBICONF.ORG | 5-6

    OCTOBER 2017
  91. (Actually, it's thanks to Category Theory) #mobiconf2017 MOBICONF.ORG | 5-6

    OCTOBER 2017
  92. #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  93. Quick summary... #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  94. In Swift, you fundamentally work in a procedural/OOP paradigm, and

    the whole language is built around that. There are some tools available to let you jump over to a functional style (without the full power of functional programming) when it’s useful. — Rob Napier #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  95. None
  96. None
  97. ❔ Qestions ❔ #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017

  98. · http://robnapier.net/swi!-is-not-functional · http://www.mokacoding.com/blog/functor- applicative-monads-in-pictures/ · https://github.com/alskipp/Swi!-Adventures-In- Monad-Land · http://khanlou.com/2017/03/that-one-optional-

    property/ · https://github.com/ijoshsmith/function-composition- in-swi! · http://learnyouahaskell.com #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017