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

Swift Generics - The 5 Stages of PATs

Swift Generics - The 5 Stages of PATs

The Swift generics system is extremely powerful. Protocols with associated types are everywhere and allow us to abstract over types with hidden type information. But they come with their complexity tax. Join David Hart in a journey through Swift's generics to better understand PATs, discover ways to overcome them, and how future Swift might resolve most of those issues.

David Hart

April 17, 2018
Tweet

More Decks by David Hart

Other Decks in Programming

Transcript

  1. Redux struct AppState { var todos: [UUID: Todo] } struct

    Todo { let uuid: UUID var text: String } protocol Action { associatedtype State func reduce(_ state: inout State) }
  2. Redux struct SetTodoText: Action { let uuid: UUID let text:

    String func reduce(_ state: inout AppState) { state.todos[uuid]!.text = text } } struct CreateTodo: Action { let uuid: UUID func reduce(_ state: inout AppState) { state.todos[uuid] = Todo(uuid: uuid, text: "New Todo") } }
  3. Redux func duplicate(todo: Todo) -> [Action] { let uuid =

    UUID() return [ CreateTodo(uuid: uuid), SetTodoText(uuid: uuid, text: todo.text) ] }
  4. Redux func duplicate(todo: Todo) -> [Action] { let uuid =

    UUID() return [ CreateTodo(uuid: uuid), SetTodoText(uuid: uuid, text: todo.text) ] } Protocol 'Action' can only be used as… Protocol 'Action' can only be used as a generic constraint because it has Self or associated type requirements
  5. Classes final class SetTodoText: Action<AppState> { let uuid: UUID let

    text: String override func reduce(_ state: inout AppState) { state.todos[uuid]!.text = text } } init(uuid: UUID, text: String) { self.uuid = uuid self.text = text }
  6. Generic Protocols // from func sort<C: Collection>(_ todos: C) where

    C.Element == Todo { … } protocol Action<State> { func reduce(_ state: inout State) } // to func sort(_ todos: Collection<Todo>) { … } // actually func sort<I>(_ todos: Collection<Todo, I>) { … }
  7. Generic Protocols protocol ConstructibleFrom<T> { init(_ value: T) } struct

    Real { ... } extension Real: ConstructibleFrom<Float> { init(_ value: Float) { ... } } extension Real: ConstructibleFrom<Double> { init(_ value: Double) { ... } }
  8. "They're these parasitic things that end up infesting your code

    in ways you really don't want." — Dave DeLong
  9. Type-Erased Wrappers protocol Action { associatedtype State func reduce(_ state:

    inout State) } private let reduceClosure: (inout State) -> Void } struct AnyAction<State>: Action { init<A: Action>(_ action: A) where A.State == State { reduceClosure = { action.reduce(&$0) } } func reduce(_ state: inout State) { reduceClosure(&state) }
  10. Type-Erased Wrappers func duplicate(todo: Todo) -> [ AnyAction( ) AnyAction<AppState>]

    { let uuid = UUID() return [ ] } AnyAction( ) CreateTodo(uuid: uuid) , SetTodoText(uuid: uuid, text: todo.text)
  11. Generalized Existentials typealias Codable = Encodable & Decodable typealias TableViewController

    = UIViewController & UITableViewDelegate typealias AnyCollection<T> = Collection where .Element == T typealias AnyAction<S> = Action where .State == S
  12. Type-Erased Wrappers func duplicate(todo: Todo) -> [AnyAction<AppState>] { let uuid

    = UUID() return [ CreateTodo(uuid: uuid), SetTodoText(uuid: uuid, text: todo.text) ] }
  13. The 5 Stages of PATs PATs are a PITA OOP

    is not dead Generic Protocols Type-erasers FTW Generalized Existentials
  14. ( ?

  15. Follow up + Come talk to me ) Generics Manifesto

    https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md * Advanced Swift https://www.objc.io/books/advanced-swift/ Rust - Advanced Traits https://doc.rust-lang.org/book/second-edition/ch19-03-advanced-traits.html Generalized Existentials Proposal https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md