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

Raheel Ahmad: Using Monads and other Functional Paradigms in Practice

1fa9cb8c7997c8c4d3d251fb5e41f749?s=47 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

1fa9cb8c7997c8c4d3d251fb5e41f749?s=128

Realm

August 25, 2016
Tweet

Transcript

  1. Raheel Ahmad 1 — Raheel Ahmad | PlanGrid

  2. Monads in Practice Building Intuition 2 — Raheel Ahmad |

    PlanGrid
  3. Outline Fundamentals of Functional Programming 3 — Raheel Ahmad |

    PlanGrid
  4. Outline Fundamentals of Functional Programming [Functor, Monad].flatMap { Definition, Example,

    Intuition } 4 — Raheel Ahmad | PlanGrid
  5. Fundamentals of FP · Immutable values · Pure functions ·

    Higher-order functions · Declarative programming 5 — Raheel Ahmad | PlanGrid
  6. let someDate: NSDate? let label: UILabel // label.text = formatted

    date 6 — Raheel Ahmad | PlanGrid
  7. 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
  8. 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
  9. if let date = someDate { label.text = formatDate(date) }

    9 — Raheel Ahmad | PlanGrid
  10. label.text = someDate.map { date in let formatter = NSDateFormatter()

    formatter.dateStyle = .ShortStyle formatter.timeStyle = .ShortStyle return formatter.stringFromDate(date) } 10 — Raheel Ahmad | PlanGrid
  11. label.text = someDate.map { formatDate($0) } 11 — Raheel Ahmad

    | PlanGrid
  12. label.text = someDate.map(formatDate) 12 — Raheel Ahmad | PlanGrid

  13. What is a Monad? 13 — Raheel Ahmad | PlanGrid

  14. Functors Basic intuition · contain a value inside a context

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

    enum Optional<Wrapped> { } enum Result<T, ErrorType> { } T -> U 15 — Raheel Ahmad | PlanGrid
  16. 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
  17. ... 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
  18. func formatDate(date: NSDate) -> String 18 — Raheel Ahmad |

    PlanGrid
  19. map over an Optional 1. let someDate: Optional<NSDate> // NSDate?

    19 — Raheel Ahmad | PlanGrid
  20. map over an Optional 1. let someDate: Optional<NSDate> // NSDate?

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

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

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

    formatDate) // Int -> String 23 — Raheel Ahmad | PlanGrid
  24. map li!s a regular function 24 — Raheel Ahmad |

    PlanGrid
  25. Stay in the Functor! dateLabel.text = someDate.map(formatDate) // vs. if

    let date = someDate { dateLabel.text = formatDate(date) } 25 — Raheel Ahmad | PlanGrid
  26. 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
  27. Compose Transformations Functors compose transformations label.text = someDate .map(formatDate) .map(upper)

    · without "side-effect"s 27 — Raheel Ahmad | PlanGrid
  28. 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
  29. Monads Basic intuition · surround a value inside a context

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

    enum Optional<Wrapped> { } struct Result<T, ErrorType> { } class Signal<Event, ErrorType> { } 30 — Raheel Ahmad | PlanGrid
  31. 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
  32. 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
  33. flatMap over an Array 33 — Raheel Ahmad | PlanGrid

  34. struct User { } struct Issue { let users: [User]

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

    } func issue(index: Int) -> Issue func collaborators(issue: Issue) -> [User] 35 — Raheel Ahmad | PlanGrid
  36. 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
  37. 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
  38. 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
  39. Another flatMap over an array ... of functions dateStringsForSection: Int

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

    -> [String] durationStringsForSection: Int -> [String] [dateStringsForSection, durationStringsForSection] .flatMap { $0(section) } 40 — Raheel Ahmad | PlanGrid
  41. 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
  42. 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
  43. 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
  44. Signal 44 — Raheel Ahmad | PlanGrid

  45. 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
  46. Signal class Signal<Value, Error> { … func on(event: Event ->

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

    Ahmad | PlanGrid
  48. Using a Signal let numbersSignal: Signal<Int, SomeError> numbersSignal.on( next: {

    print("Received \($0)") }, failed: { print("Error \($0)") } ) 48 — Raheel Ahmad | PlanGrid
  49. 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
  50. flatMap over a Signal extension Signal { func flatMap<U>(transform: Value

    -> Signal<U, Error>) -> Signal<U, Error> } 50 — Raheel Ahmad | PlanGrid
  51. 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
  52. Providing a common pattern · recognize flatMapping across different ·

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

    · Monad: chain operations on contained value 53 — Raheel Ahmad | PlanGrid
  54. Theoretically Speaking 54 — Raheel Ahmad | PlanGrid

  55. 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
  56. 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
  57. 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
  58. THANK YOU! @raheel | PlanGrid.com Questions? 58 — Raheel Ahmad

    | PlanGrid