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

Raheel Ahmad: Using Monads and other Functional...

Realm
August 25, 2016

Raheel Ahmad: Using Monads and other Functional Paradigms in Practice

Monads can be a great addition to your programming toolbox: they provide an abstraction and a design pattern that help in writing more composable and declarative code. The path to Monad Enlightenment, however, is either paved with "learn Haskell" or "it's just flatMap". This talk will help build an intuition for what Monads are in the context of Functional Programming, and also why (and if!) you should care about them.
Bio: Raheel currently works at PlanGrid on their iOS app. Previously he helped rewrite the Level Money app in Swift. He enjoys working on the iOS platform, but also indulge in web frameworks, Haskell, information visualization, and typography.
Twitter: https://twitter.com/raheel

Realm

August 25, 2016
Tweet

More Decks by Realm

Other Decks in Technology

Transcript

  1. Fundamentals of FP · Immutable values · Pure functions ·

    Higher-order functions · Declarative programming 5 — Raheel Ahmad | PlanGrid
  2. let someDate: NSDate? let label: UILabel func formatDate(date: NSDate) ->

    String { let formatter = NSDateFormatter() formatter.dateStyle = .ShortStyle formatter.timeStyle = .ShortStyle return formatter.stringFromDate(date) } 7 — Raheel Ahmad | PlanGrid
  3. let someDate: NSDate? let label: UILabel func formatDate(date: NSDate) ->

    String { let formatter = NSDateFormatter() formatter.dateStyle = .ShortStyle formatter.timeStyle = .ShortStyle return formatter.stringFromDate(date) } // label.text = formatDate(someDate) ╳ 8 — Raheel Ahmad | PlanGrid
  4. label.text = someDate.map { date in let formatter = NSDateFormatter()

    formatter.dateStyle = .ShortStyle formatter.timeStyle = .ShortStyle return formatter.stringFromDate(date) } 10 — Raheel Ahmad | PlanGrid
  5. Functors Basic intuition · contain a value inside a context

    · provides an interface to transform/"map" the contained value 14 — Raheel Ahmad | PlanGrid
  6. Surround a value inside a context struct Array<Element> { }

    enum Optional<Wrapped> { } enum Result<T, ErrorType> { } T -> U 15 — Raheel Ahmad | PlanGrid
  7. Allow mapping the contained value extension Array { func map<U>(transform:

    Element -> U) -> [U] } enum Optional<Wrapped> { func map<U>(transform: Wrapped -> U) -> Optional<U> } enum Result<Value, ErrorType> { func map<U>(transform: Value -> U) -> Result<U, ErrorType> } 16 — Raheel Ahmad | PlanGrid
  8. ... map for function func map<T, U, V>(f: T ->

    U, _ transform: U -> V) -> (T -> V) { return { t in transform(f(t)) } } 17 — Raheel Ahmad | PlanGrid
  9. map over an Optional 1. let someDate: Optional<NSDate> // NSDate?

    2. someDate.map(formatDate) // Optional<String> 20 — Raheel Ahmad | PlanGrid
  10. map over an Array 1. let dates = [ yesterday,

    today, tomorrow ] // [NSDate] 2. dates.map(formatDate) // [String] 21 — Raheel Ahmad | PlanGrid
  11. map over a Result 1. let resultingDate: Result<NSDate, NSError> 2.

    resultingDate.map(formatDate) // Result<String, NSError> 22 — Raheel Ahmad | PlanGrid
  12. map over a function let dateAtSection: Int -> Date map(dateAtSection,

    formatDate) // Int -> String 23 — Raheel Ahmad | PlanGrid
  13. Stay in the Functor! dateLabel.text = someDate.map(formatDate) // vs. if

    let date = someDate { dateLabel.text = formatDate(date) } 25 — Raheel Ahmad | PlanGrid
  14. Contain transformations · not writing the map code everywhere ·

    like we used to in Objective-C · simple for Array & Optional, more complex for other functors 26 — Raheel Ahmad | PlanGrid
  15. Functors of your own · Intuitive for generic data structures,

    e.g. Tree · Possibly for custom data structure · Cache<T> → map → Cache<U> 28 — Raheel Ahmad | PlanGrid
  16. Monads Basic intuition · surround a value inside a context

    · allow mapping and then flattening the contained value 29 — Raheel Ahmad | PlanGrid
  17. Surround a value inside a context struct Array<Element> { }

    enum Optional<Wrapped> { } struct Result<T, ErrorType> { } class Signal<Event, ErrorType> { } 30 — Raheel Ahmad | PlanGrid
  18. Allow flatMapping the contained value extension Array { func flatMap<U>(transform:

    Element -> [U]) -> [U] } enum Optional<Wrapped> { func flatMap<U>(transform: (Wrapped) -> U?) -> U? } struct Result<Value, E> { func flatMap<U>(transform: Value -> Result<U, E>) -> Result<U, E> } extension Signal { func flatMap<U>(transform: Value -> Signal<U, Error>) -> Signal<U, Error> } 31 — Raheel Ahmad | PlanGrid
  19. map extension Array { func map<U>(transform: Element -> U) ->

    [U] } enum Optional<Wrapped> { func map<U>(transform: Wrapped -> U) -> Optional<U> } struct Result<Value, ErrorType> { func map<U>(transform: Value -> U) -> Result<U, ErrorType> } extension Signal { func map<U>(transform: Value -> U) -> Signal<U, Error> } 32 — Raheel Ahmad | PlanGrid
  20. struct User { } struct Issue { let users: [User]

    } 34 — Raheel Ahmad | PlanGrid
  21. struct User { } struct Issue { let users: [User]

    } func issue(index: Int) -> Issue func collaborators(issue: Issue) -> [User] 35 — Raheel Ahmad | PlanGrid
  22. struct User { ... } struct Issue { let users:

    [User] } func issue(index: Int) -> Issue func collaborators(issue: Issue) -> [User] let selectedRows: [Int] // selected collaborators 36 — Raheel Ahmad | PlanGrid
  23. struct User { ... } struct Issue { let users:

    [User] } func issue(index: Int) -> Issue func collaborators(issue: Issue) -> [User] let selectedRows: [Int] // selected collaborators selectedRows .map(issue) // [Issue] .map(collaborators) // [ [User] ] 37 — Raheel Ahmad | PlanGrid
  24. struct User { ... } struct Issue { let users:

    [User] } func issue(index: Int) -> Issue func collaborators(issue: Issue) -> [User] let selectedRows: [Int] // selected collaborators selectedRows .map(issue) // [Issue] .flatMap(collaborators) // [User] 38 — Raheel Ahmad | PlanGrid
  25. Another flatMap over an array ... of functions dateStringsForSection: Int

    -> [String] durationStringsForSection: Int -> [String] 39 — Raheel Ahmad | PlanGrid
  26. Another flatMap over an array ... of functions dateStringsForSection: Int

    -> [String] durationStringsForSection: Int -> [String] [dateStringsForSection, durationStringsForSection] .flatMap { $0(section) } 40 — Raheel Ahmad | PlanGrid
  27. flatMap over an Optional func userForName(name: String) -> User? func

    avatarForUser(user: User) -> UIImage? let myName = "Raheel" // Compute the image for the given name 41 — Raheel Ahmad | PlanGrid
  28. flatMap over an Optional func userForName(name: String) -> User? func

    avatarForUser(user: User) -> UIImage? let myName = "Raheel" userForName(myName).flatMap(avatarForUser) 42 — Raheel Ahmad | PlanGrid
  29. flatMap over a Result func userForName(name: String) -> Result<User, NSError>

    func avatarForUser(user: User) -> Result<UIImage, NSError> let myName = "Raheel" userForName(myName).flatMap(avatarForUser) 43 — Raheel Ahmad | PlanGrid
  30. Event values represent what a signal "emits" enum Event<Value, Error>

    { case Next(Value) case Failed(Error) case Completed case Interrupted } 45 — Raheel Ahmad | PlanGrid
  31. Signal class Signal<Value, Error> { … func on(event: Event ->

    (), next: Value -> (), failed: Error -> (), completed: () -> (), interrupted: () -> ()) } 46 — Raheel Ahmad | PlanGrid
  32. Using a Signal let numbersSignal: Signal<Int, SomeError> numbersSignal.on( next: {

    print("Received \($0)") }, failed: { print("Error \($0)") } ) 48 — Raheel Ahmad | PlanGrid
  33. flatMap over a Signal func userForPredicate(predicate: Predicate) -> Signal<User, NSError>

    func avatarForUser(user: User) -> Signal<UIImage, NSError> let predicate: Predicate 49 — Raheel Ahmad | PlanGrid
  34. flatMap over a Signal extension Signal { func flatMap<U>(transform: Value

    -> Signal<U, Error>) -> Signal<U, Error> } 50 — Raheel Ahmad | PlanGrid
  35. flatMap over a Signal func userForPredicate(predicate: Predicate) -> Signal<User, NSError>

    func avatarForUser(user: User) -> Signal<UIImage, NSError> let predicate: Predicate userForPredicate(predicate) // Signal<User, NSError> .flatMap(avatarForUser) // Signal<UIImage, NSError> 51 — Raheel Ahmad | PlanGrid
  36. Providing a common pattern · recognize flatMapping across different ·

    contexts · problems · disciplines 52 — Raheel Ahmad | PlanGrid
  37. Transformations vs. Operations · Functor: chain transformations of contained value

    · Monad: chain operations on contained value 53 — Raheel Ahmad | PlanGrid
  38. Monad requirements (Haskell-ish) Monads require a pure function func pure<T>(m:

    T) -> Optional<T> { return Optional<T>(m) } // let m: Int? = 23 · and satisfy basic Monadic laws · i.e. Set is not a Monad 55 — Raheel Ahmad | PlanGrid
  39. Monads in Swift · No real Monad type (protocol) in

    Swi! · need Higher Kinded Types · see Scala & Scalaz · Methods vs. functions / PoP vs. Functional 56 — Raheel Ahmad | PlanGrid
  40. Does it matter · maybe not if you are an

    app developer · maybe yes if you write libraries · pattern recognition matters · shared vocabulary matters · gateway to FP 57 — Raheel Ahmad | PlanGrid