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

Generics and Inference

Generics and Inference

This talk will explore inference from the perspective of protocols and generics and is based off a series of blog posts I've written(foxinswift.com) on the topic. In the first part of my talk casting number types through inference. I'll then show you struct serialization example demonstrating inferring a type through a mapping function. My last example will take you through inferring an associatedtype on a barebones promise implementation and we'll use it to in the context of making a network request. To finish things off I'll briefly speak on what's new in swift generics and some limitations of those features.

Rich Fox

July 28, 2016
Tweet

More Decks by Rich Fox

Other Decks in Programming

Transcript

  1. <Generics: Inference> → Rich Fox @RGfox → I work at

    Propeller → I am a contributer to
  2. Generics and the Art of Inferences → What are generics

    → What is inference → Goal of Talk
  3. protocol NumberConvertible { init(_ value: Int) init(_ value: Float) init(_

    value: Double) } extension Double: NumberConvertible {} extension Float: NumberConvertible {} extension Int: NumberConvertible {} Int,Float,Double: NumberConvertible
  4. extension NumberConvertible { func convert<T: NumberConvertible>() -> T { switch

    self { case let x as Float: return T(x) case let x as Int: return T(x) case let x as Double: return T(x) default: fatalError("NumberConvertible convert failed!") } } }
  5. Cast by Inference! let number = 5.5 let a: Float

    = number.convert() let b: Int = number.convert() let c: Double = number.convert() let aa = number.convert() + Float(2) let bb = number.convert() + Int(2) let cc = number.convert() + Double(2)
  6. protocol Serializable { init(construct: [String: Any]) func destruct() -> [String:

    Any] } extension Optional { func unbox<T>() -> T? { return self as? T } func unboxArray<T>() -> [T] { return unbox() ?? [] } }
  7. struct SortItem { let name: String let subSorts: [SortItem] }

    extension SortItem: Serializable { func destruct() -> [String: Any] { var construct = [String: Any]() construct["name"] = name construct["subSorts"] = subSorts.map { $0.destruct() } return construct } init(construct: [String: Any]) { name = construct["name"].unbox() ?? "" let sorts = construct["subSorts"] .unboxArray() !.map(SortItem.init) } }
  8. struct SortItem { let name: String let subSorts: [SortItem] }

    extension SortItem: Serializable { func destruct() -> [String: Any] { var construct = [String: Any]() construct["name"] = name construct["subSorts"] = subSorts.map { $0.destruct() } return construct } init(construct: [String: Any]) { name = construct["name"].unbox() ?? "" let sorts = construct["subSorts"] .unboxArray() !.map(SortItem.init(construct:)) } }
  9. struct SortItem { let name: String let subSorts: [SortItem] //Default

    Value = Incognito init(subSorts: [SortItem] = [], name: String) { self.subSorts = subSorts self.name = name } } extension SortItem: Serializable { func destruct() -> [String: Any] { var construct = [String: Any]() construct["name"] = name construct["subSorts"] = subSorts.map { $0.destruct() } return construct } init(construct: [String: Any]) { name = construct["name"].unbox() ?? "" let sorts = construct["subSorts"] .unboxArray() !.map(SortItem.init) } }
  10. struct SortItem { let name: String let subSorts: [SortItem] //Default

    Value = Incognito init(subSorts: [SortItem] = [], name: String) { self.subSorts = subSorts self.name = name } } extension SortItem: Serializable { func destruct() -> [String: Any] { var construct = [String: Any]() construct["name"] = name construct["subSorts"] = subSorts.map { $0.destruct() } return construct } init(construct: [String: Any]) { name = construct["name"].unbox() ?? "" let sorts = construct["subSorts"] .unboxArray() .map(SortItem.init) subSorts = sorts } }
  11. Return type of unboxArray() inferred through .map! let sorts =

    construct["subSorts"] .unboxArray() .map(SortItem.init(construct:))
  12. enum Result<T> { case error(BasicError), some(T) private func fire(target: Promise<T>)

    { switch self { case .error(let err): target.failedAction?(err) case .some(let val): target.completeAction?(val) } } }
  13. final class Promise<T> { private var completeAction: (T -> Void)?

    private var failedAction: (BasicError -> Void)? func complete(action: T! -> Void) -> Promise<T> { completeAction = action return self } func failure(action: BasicError -> Void) -> Promise<T> { failedAction = action return self } var trigger: Result<T>? { didSet { trigger?.fire(self) } } } Closure keeps Promise alive - waiting for trigger
  14. Promise on the Network: func getUser(url: String) -> Promise<User> {

    let promise = Promise<User>() return promise }
  15. Promise on the Network: func getUser(url: String) -> Promise<User> {

    let promise = Promise<User>() //Async call keeping reference to ˆpromiseˆ makeGetRequest(url) { response in } return promise }
  16. Promise on the Network: func getUser(url: String) -> Promise<User> {

    let promise = Promise<User>() //Async call keeping reference to ˆpromiseˆ makeGetRequest(url) { response in guard let json = response else { Promise.trigger = Result.error(.unknown) return } } return promise }
  17. Promise on the Network: func getUser(url: String) -> Promise<User> {

    let promise = Promise<User>() //Async call keeping reference to ˆpromiseˆ makeGetRequest(url) { response in guard let json = response else { Promise.trigger = Result.error(.unknown) return } if let user = try? User(object: json) { Promise.trigger = Result.some(user) } } return promise }
  18. Promise on the Network: func getUser(url: String) -> Promise<User> {

    let promise = Promise<User>() //Async call keeping reference to ˆpromiseˆ makeGetRequest(url) { response in guard let json = response else { Promise.trigger = Result.error(.unknown) return } if let user = try? User(object: json) { Promise.trigger = Result.some(user) } else { let error = BasicError(object: json) Promise.trigger = Result.error(error) } } return promise }
  19. More Generics and Inferred Promises func getEncodableType<T: JSONDecodable>(url: String) ->

    Promise<T> { let promise = Promise<T>() //Async call keeping reference to ˆpromiseˆ makeGetRequest(url) { response in guard let json = response else { Promise.trigger = Result.error(.unknown) return } if let result = try? T(object: json) { Promise.trigger = Result.some(result) } else { let error = BasicError(object: json) Promise.trigger = Result.error(error) } } return promise }
  20. More Generics and Inferred Promises func getEncodableType<T: JSONDecodable>(url: String) ->

    Promise<T> { let promise = Promise<T>() //Generic instead of User //Async call keeping reference to ˆpromiseˆ makeGetRequest(url) { response in guard let json = response else { Promise.trigger = Result.error(.unknown) return } if let result = try? T(object: json) { //JSONDecodable init Promise.trigger = Result.some(result) } else { let error = BasicError(object: json) Promise.trigger = Result.error(error) } } return promise }
  21. var user:User? var guest:Guest? func fetchPeople() { let printError: BasicError

    -> Void = {"error: \($0.message)"} NetworkService.getEncodableType("/url/User") .complete { user = $0 } .failure(printError) NetworkService.getEncodableType("/url/Guest") .complete { guest = $0 } .failure(printError) }
  22. New in Swift 3 Generics Generic typealias typealias StringDictionary<T> =

    Dictionary<String, T> typealias BackwardTriple<T1,T2,T3> = (T3, T2, T1) Limited: Generic closure Crashes Playground typealias StringDictionaryValue<T> = (Dictionary<String, T>) -> T? let testValue: StringDictionaryValue = { return $0["test"] }
  23. New in Swift 3 Generics Generic typealias typealias StringDictionary<T> =

    Dictionary<String, T> typealias BackwardTriple<T1,T2,T3> = (T3, T2, T1) Function Works as Expected typealias StringDictionary<T> = (Dictionary<String, T>) func valueForKey<T>(dict:StringDictionary<T>, key: String) -> T? { return dict[key] }
  24. 1. Return Type Context func convert<T: NumberConvertible>() -> T 2.

    Though map Function .unboxArray() .map(SortItem.init(construct:)) 3. Through associatedtype Context func complete(action: T! -> Void) -> Promise<T>