Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Generics and Inference

93 views

Published on

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.

Published in: Software
  • Be the first to comment

  • Be the first to like this

Generics and Inference

  1. 1. <Generics: Inference>
  2. 2. <Generics: Inference> → Rich Fox @RGfox → I work at Propeller → I am a contributer to
  3. 3. Generics and the Art of Inferences → What are generics → What is inference → Goal of Talk
  4. 4. Ex 1. Casting Number Types
  5. 5. protocol NumberConvertible { init(_ value: Int) init(_ value: Float) init(_ value: Double) } extension Double: NumberConvertible {} extension Float: NumberConvertible {} extension Int: NumberConvertible {} Int,Float,Double: NumberConvertible
  6. 6. 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!") } } }
  7. 7. 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)
  8. 8. Ex 2. Encoding/Decoding Structs (Map and Inferences)
  9. 9. 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() ?? [] } }
  10. 10. 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) } }
  11. 11. 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:)) } }
  12. 12. 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) } }
  13. 13. 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 } }
  14. 14. Return type of unboxArray() inferred through .map! let sorts = construct["subSorts"] .unboxArray() .map(SortItem.init(construct:))
  15. 15. Ex 3: Promises/Networking
  16. 16. Result/Promise → Concise implementation → Result Enum → Promise Class w/AssociateType
  17. 17. 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) } } }
  18. 18. 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
  19. 19. Usage Example: (Networking + Generics * Inference)
  20. 20. Promise on the Network: func getUser(url: String) -> Promise<User> { }
  21. 21. Promise on the Network: func getUser(url: String) -> Promise<User> { let promise = Promise<User>() return promise }
  22. 22. 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 }
  23. 23. 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 }
  24. 24. 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 }
  25. 25. 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 }
  26. 26. 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 }
  27. 27. 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 }
  28. 28. 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) }
  29. 29. Promise<T> inferred by complete:(T)->Void NetworkService.getEncodableType("/url/User") .complete { user = $0 }
  30. 30. 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"] }
  31. 31. 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] }
  32. 32. Inference Concluded
  33. 33. 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>
  34. 34. Thank You Forward Swift @RGfox

×