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

Finally Understand Monads with this One Weird Trick

Finally Understand Monads with this One Weird Trick

A talk about understanding monoids, functors, and monads by translating Haskell into Swift.

D968adc8f0c51ec3e5949a6737b9247f?s=128

Andy Bartholomew

March 13, 2016
Tweet

Transcript

  1. Understand Monads with this One Weird Trick Andy Bartholomew Airbnb

    @jqsilver github.com/jqsilver
  2. (the trick is to translate Haskell code into Swift) ~>

  3. Efficient JSON in Swift with Functional Concepts and Generics Tony

    DePasquale (@TonyD256) “The functional programming concepts Monads, Applicative Functors, and Currying will help to condense this...” Runes
  4. Functors? Monads?

  5. Learn You A Haskell

  6. Haskell is hard to read class Functor f where fmap

    :: (a -> b) -> f a -> f b instance Functor ((->) r) where fmap = (.) class (Functor f) => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b
  7. The Formula Describe in Natural Language Write Type Signature Name

    everything Implement logic Test your code Not necessarily in this order
  8. class ~> protocol ~>

  9. ❏ Monoid ❏ Functor ❏ Monad

  10. Monoid class Monoid m where mempty :: m mappend ::

    m -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty
  11. Monoid ~> Monoid class Monoid m where mempty :: m

    mappend :: m -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty protocol Monoid { }
  12. Monoid ~> Monoid class Monoid m where mempty :: m

    mappend :: m -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty protocol Monoid { static func empty() -> m static func append(m, m) -> m static func concat([m]) -> m }
  13. What is m? class Monoid m where mempty :: m

    mappend :: m -> m -> m mconcat :: [m] -> m
  14. m ~> Self class Monoid m where mempty :: m

    mappend :: m -> m -> m mconcat :: [ m] -> m mconcat = foldr mappend mempty protocol Monoid { static func empty() -> Self static func append(Self, Self) -> Self static func concat([Self]) -> Self }
  15. class Monoid m where mempty :: m mappend :: m

    -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty protocol Monoid { static func empty() -> Self static func append(left: Self, right: Self) -> Self static func concat(list: [Self]) -> Self } Named arguments
  16. Concat class Monoid m where mempty :: m mappend ::

    m -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty protocol Monoid { static func makeEmpty() -> Self static func concat(left: Self, right: Self) -> Self static func concat(list: [Self]) -> Self }
  17. ❏ Monoid Concattable ❏ Functor ❏ Monad

  18. Monoid means Concattable protocol Concattable { static func makeEmpty() ->

    Self static func concat(left: Self, right: Self) -> Self static func concat(list: [Self]) -> Self }
  19. init() protocol Concattable { init() static func concat(left: Self, right:

    Self) -> Self static func concat(list: [Self]) -> Self }
  20. + protocol Concattable { init() func +(left: Self, right: Self)

    -> Self static func concat(list: [Self]) -> Self }
  21. mconcat = foldr mappend mempty extension Concattable { static func

    concat(list: [Self]) -> Self { return list.reduce(Self(), combine: +) } }
  22. concrete implementations extension Int: Concattable {} extension Float: Concattable {}

    extension String: Concattable {} extension Array: Concattable {}
  23. More implementations func +(left: Set, right: Set) { return left.union(right)

    } func +(left: Bool, right: Bool) { return left || right } http://learnyouahaskell.com/functors-applicative-functors-and-monoids#monoids
  24. ❏ Monoid Concattable ❏ Functor ❏ Monad

  25. Functor class Functor f where fmap :: (a -> b)

    -> f a -> f b
  26. ❏ Monoid Concattable ❏ Functor Mappable ❏ Monad

  27. Functor ~> Mappable class Functor f where fmap :: (a

    -> b) -> f a -> f b protocol Mappable { static func map(a -> b, f a) -> f b }
  28. f ~> Self class Functor f where fmap :: (a

    -> b) -> f a -> f b protocol Mappable { static func map(a -> b, Self a) -> Self b }
  29. naming class Functor f where fmap :: (a -> b)

    -> f a -> f b protocol Mappable { static func map(transform: a -> b, input: Self a) -> Self b }
  30. what is f a? class Functor f where fmap ::

    (a -> b) -> f a -> f b protocol Mappable { static func map(transform: a -> b, input: Self a) -> Self b }
  31. Parameterized Types, aka Generics struct Array<Element> { … } let

    x = Array // Invalid let x = Array<Int> protocol SequenceType { typealias associatedtype Element }
  32. f a ~> Mappable<A> class Functor f where fmap ::

    (a -> b) -> f a -> f b protocol Mappable { associatedtype Element static func map<A, B>(transform: A -> B, input: Self<A>) -> Self<B> }
  33. a ~> InType b ~> OutType class Functor f where

    fmap :: (a -> b) -> f a -> f b protocol Mappable { associatedtype Element static func map<InType, OutType>( transform: InType -> OutType, input: Self<InType>) -> Self<OutType> }
  34. input ~> self protocol Mappable { associatedtype Element func map<OutType>(transform:

    Element -> OutType) -> Self<OutType> }
  35. input ~> self protocol Mappable { associatedtype Element func map<OutType>(transform:

    Element -> OutType) -> Self<OutType> } protocol SequenceType { typealias Element func map<OutType>(transform: Element -> OutType) -> [OutType] }
  36. “cannot specialize non-generic type Self” protocol Mappable { associatedtype Element

    func map<OutType>(transform: Element -> OutType) -> Self<OutType> } protocol SequenceType { typealias Element func map<OutType>(transform: Element -> OutType) -> [OutType] }
  37. trying type constraints protocol Mappable { associatedtype Element func map<OutType,

    OutMappable: Mappable where Element == OutType>( transform: (Element -> OutType) ) -> OutMappable }
  38. concrete implementation struct Box<ContainedType>: Mappable { var contained: ContainedType associatedtype

    Element = ContainedType }
  39. nope struct Box<ContainedType>: Mappable { var contained: ContainedType associatedtype Element

    = ContainedType }
  40. Mappable protocol Mappable { associatedtype Element func map<OutType>(transform: Element ->

    OutType) -> Self<OutType> }
  41. Concrete Implementations extension Array: Mappable { func map<OutType>(transform: Element ->

    OutType) -> [OutType] } extension Optional: Mappable { func map<OutType>(transform: Wrapped -> OutType) -> OutType? } let maybeValue = maybeObject.map { $0.property } let maybeValue = maybeObject?.property
  42. Result Enum enum Result<ValueType> { case Failure(error: ErrorType) case Success(value:

    ValueType) func map<OutType>(transform: ValueType -> OutType) -> Result<OutType> { switch self { case .Failure(let error): return .Failure(error) case .Success(let value): return .Success(value: transform(value)) } }
  43. ❏ Monoid Concattable ❏ Functor Mappable ❏ Monad

  44. Monad class Monad m where return :: a -> m

    a (>>=) :: m a -> (a -> m b) -> m b
  45. ❏ Monoid Concattable ❏ Functor Mappable ❏ Monad Bindable

  46. Monad ~> Bindable class Monad m where return :: a

    -> m a (>>=) :: m a -> (a -> m b) -> m b protocol Bindable: Mappable { init(element: Element) static func bind<Out>( input: Self<In>, transform: In -> Self<Out>) -> Self<Out> }
  47. Monad ~> Bindable class Monad m where return :: a

    -> m a (>>=) :: m a -> (a -> m b) -> m b protocol Bindable: Mappable { init(element: Element) func bind<Out>(transform: Element -> Self<Out>) -> Self<Out> }
  48. Optional as Bindable extension Optional: Bindable { associatedtype Element =

    Wrapped func bind<Out>(transform: Wrapped -> Out?) -> Out? { if let value = self { return transform(value) } else { return nil } } } let maybeResult = maybeValue.bind(someMethod)
  49. Array as Bindable extension Array: Bindable { func bind<Out>(transform: Element

    -> [Out]) -> [Out] { let arrayOfArrays: [[Out]] = self.map(transform) return arrayOfArrays.flatten } }
  50. Array as Bindable extension Array: Bindable { func bind<Out>(transform: Element

    -> [Out]) -> [Out] { return flatMap(transform) } }
  51. Back to Optional func someTransform(val: InType) -> OutType? let result1:

    OutType?? = maybeValue.map(someTransform) let result2: OutType? = maybeValue.flatMap(someTransform)
  52. Optional Chaining as Monad // property: OutType? let result1: OutType??

    = maybeObject.map { $0.property } let result2: OutType? = maybeObject.flatMap { $0.property } let result3= maybeObject?.property let maybeValue: String? = maybeObject?.property?.stringValue
  53. Monad ~> FlatMappable class Monad m where return :: a

    -> m a (>>=) :: m a -> (a -> m b) -> m b protocol FlatMappable: Mappable { init(element: Element) func flatMap<Out>(transform: Element -> Self<Out>) -> Self<Out> }
  54. ❏ Monoid Concattable ❏ Functor Mappable ❏ Monad Bindable FlatMappable

  55. Wait, that’s it?

  56. Basically, yeah.

  57. maybeObject?.property?.value

  58. Container Types

  59. Break it down

  60. Further reading

  61. Functions as Container Types instance Functor ((->) r) where fmap

    f g = (\x -> f (g x)) instance Applicative ((->) r) where pure x = (\_ -> x) f <*> g = \x -> f x (g x)
  62. Type Class Laws as Unit Tests fmap (p . q)

    = (fmap p) . (fmap q) func assertComposition(mappable: SomeMappable, ...) { let composedTransforms: FirstIn -> SecondOut = { ... return secondTransform(firstTransform(input)) } let dotThenMap = mappable.map(composedTransforms) let mapThenDot = mappable.map(firstTransform) .map(secondTransform) assertEquals(dotThenMap, mapThenDot) }
  63. Proposing New Swift Syntax Maybe: someFunction.curry.bind(arg1)?.bind(arg2).run() Or Maybe: someFunction(if let

    maybeVal, arg2: if let maybeVal2) Or Maybe: @nullfriendly func someFunction(arg1: Int) -> Int
  64. Naming Matters

  65. Thank You andy.bartholomew@gmail.com twitter/github: @jqsilver