generics and inference
TRANSCRIPT
<Generics: Inference>
<Generics: Inference>→ Rich Fox @RGfox→ I work at Propeller→ I am a contributer to
Generics and the Art of Inferences→ What are generics
→ What is inference
→ Goal of Talk
Ex 1. Casting Number Types
protocol NumberConvertible { init(_ value: Int) init(_ value: Float) init(_ value: Double)}
extension Double: NumberConvertible {}extension Float: NumberConvertible {}extension Int: NumberConvertible {}
Int,Float,Double: NumberConvertible
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!") } }}
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)
Ex 2. Encoding/Decoding Structs(Map and Inferences)
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() ?? [] }}
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)
}}
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:))
}}
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)
}}
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 }}
Return type of unboxArray() inferred through .map!
let sorts = construct["subSorts"] .unboxArray() .map(SortItem.init(construct:))
Ex 3: Promises/Networking
Result/Promise→ Concise implementation
→ Result Enum→ Promise Class w/AssociateType
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) } }}
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
Usage Example:(Networking + Generics * Inference)
Promise on the Network:func getUser(url: String) -> Promise<User> {
}
Promise on the Network:func getUser(url: String) -> Promise<User> { let promise = Promise<User>()
return promise}
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}
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}
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}
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}
More Generics and Inferred Promisesfunc 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}
More Generics and Inferred Promisesfunc 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}
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)}
Promise<T> inferred by complete:(T)->Void
NetworkService.getEncodableType("/url/User") .complete { user = $0 }
New in Swift 3 GenericsGeneric 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"] }
New in Swift 3 GenericsGeneric 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]}
Inference Concluded
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>
Thank YouForward Swift
@RGfox