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. Agenda: OOP + iOS + (FP + Swift) = #mobiconf2017

    MOBICONF.ORG | 5-6 OCTOBER 2017
  2. 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
  3. What's so special about FP, that iOS dev should take

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

    iOS pitfalls! #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  5. 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
  6. 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
  7. However, it isn't about mathematics, but about abstraction and reducing

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

    Mutable State 3. Types Taken from Functional Swi!
  9. 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
  10. 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
  11. It doesn't mean we can't find any functional patterns in

    Swift! #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  12. (...) 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
  13. We just need to find this misuse and start using

    it on purpose. #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  14. 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
  15. 1. Takes one or more functions as arguments 2. Returns

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

    drop contains sorted #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  17. // 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
  18. 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
  19. 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
  20. 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
  21. // 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
  22. An act or mechanism to combine simple functions to build

    more complicated ones. — Wikipedia #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  23. // 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!
  24. -- 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
  25. // 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!
  26. open class NSArray: NSObject, CKRecordValue, CustomReflectable, CVarArg, Equatable, ExpressibleByArrayLiteral, Hashable,

    NSCopying, NSFastEnumeration, NSMutableCopying, NSSecureCoding, Sequence #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  27. Way of creating the type composition in the world without

    inheritance. — Krzysztof Siejkowski #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  28. -- 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
  29. -- 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
  30. Haskell's Typeclasses key to success is the amount of instances

    provided by basic libraries! #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  31. 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
  32. Next time you write some code, please think about this...

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

    work for all Ys as well? #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  34. 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
  35. 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
  36. // 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
  37. Optionality can sometimes mean more than just an existence. —

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

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

    take math classes again? #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  40. 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
  41. 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
  42. Go and read "That One Optional Property" by Soroush Khanlou

    #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  43. 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
  44. 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
  45. // 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
  46. But you've said the same thing about Higher Order Functions...

    #mobiconf2017 MOBICONF.ORG | 5-6 OCTOBER 2017
  47. 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