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.

Andy Bartholomew

March 13, 2016
Tweet

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. The Formula Describe in Natural Language Write Type Signature Name

    everything Implement logic Test your code Not necessarily in this order
  4. Monoid class Monoid m where mempty :: m mappend ::

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

    mappend :: m -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty protocol Monoid { }
  6. 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 }
  7. What is m? class Monoid m where mempty :: m

    mappend :: m -> m -> m mconcat :: [m] -> m
  8. 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 }
  9. 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
  10. 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 }
  11. Monoid means Concattable protocol Concattable { static func makeEmpty() ->

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

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

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

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

    extension String: Concattable {} extension Array: Concattable {}
  16. 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
  17. Functor ~> Mappable class Functor f where fmap :: (a

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

    -> b) -> f a -> f b protocol Mappable { static func map(a -> b, Self a) -> Self b }
  19. 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 }
  20. 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 }
  21. Parameterized Types, aka Generics struct Array<Element> { … } let

    x = Array // Invalid let x = Array<Int> protocol SequenceType { typealias associatedtype Element }
  22. 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> }
  23. 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> }
  24. 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] }
  25. “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] }
  26. trying type constraints protocol Mappable { associatedtype Element func map<OutType,

    OutMappable: Mappable where Element == OutType>( transform: (Element -> OutType) ) -> OutMappable }
  27. 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
  28. 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)) } }
  29. Monad class Monad m where return :: a -> m

    a (>>=) :: m a -> (a -> m b) -> m b
  30. 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> }
  31. 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> }
  32. 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)
  33. Array as Bindable extension Array: Bindable { func bind<Out>(transform: Element

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

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

    OutType?? = maybeValue.map(someTransform) let result2: OutType? = maybeValue.flatMap(someTransform)
  36. 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
  37. 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> }
  38. 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)
  39. 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) }
  40. 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