I just mentioned this in my other email, but to point out here: the reason this 
works in your case is because you adopt these methods as static funcs and can 
reasonably rely on subclasses of NSData, NSNumber, NSString, etc. to do the 
right thing because of work done behind the scenes in the ObjC implementations 
of these classes (and because we’ve got established subclassing requirements on 
these methods — all subclasses of these classes are going to look approximately 
the same without doing anything crazy).

This would not work for Codable in the general case, however, where subclasses 
likely need to add additional storage, properties, encoded representations, 
etc., without equivalent requirements, either via additional protocols or 
conventions.

> On Aug 3, 2017, at 1:50 AM, Gwendal Roué via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> 
>> Le 3 août 2017 à 02:09, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :
>> 
>> P.S. There's a reason why Decodable uses an initializer instead of a 
>> factory-like method on the type but I can't remember what it is right now. I 
>> think it's something to do with having the right result type, which would 
>> have to be either 'Any' or an associated type if it wasn't just 'Self'. (And 
>> if it is 'Self' then it has all the same problems as an initializer and 
>> would require extra syntax.) Itai would know for sure.
> 
> For anyone interested, factory methods *can* retroactivaly be added to 
> existing classes. This is how the SQLite library GRDB.swift is able to decode 
> classes hierarchies like NSString, NSNumber, NSDecimalNumber, etc. from 
> SQLite values:
> 
> The protocol for types that can instantiate from SQLite values has a factory 
> method:
> 
>     public protocol DatabaseValueConvertible {
>         /// Returns a value initialized from *dbValue*, if possible.
>         static func fromDatabaseValue(_ dbValue: DatabaseValue) -> Self?
>     }
> 
> Having Foundation classes implement it uses various techniques:
> 
> 1. "Casting" (Data to NSData, or NSDate to Date, depending on which type 
> provides the root conformance)
> 
>     // Workaround Swift inconvenience around factory methods of non-final 
> classes
>     func cast<T, U>(_ value: T) -> U? {
>         return value as? U
>     }
>     
>     extension NSData : DatabaseValueConvertible {
>         public static func fromDatabaseValue(_ dbValue: DatabaseValue) -> 
> Self? {
>             // Use Data conformance
>             guard let data = Data.fromDatabaseValue(dbValue) else {
>                 return nil
>             }
>             return cast(data)
>         }
>     }
> 
>     // Derives Date conformance from NSDate, for example
>     extension ReferenceConvertible where Self: DatabaseValueConvertible, 
> Self.ReferenceType: DatabaseValueConvertible {
>         public static func fromDatabaseValue(_ dbValue: DatabaseValue) -> 
> Self? {
>             return ReferenceType.fromDatabaseValue(dbValue).flatMap { 
> cast($0) }
>         }
>     }
> 
> 
> 2. Using magic Foundation initializers (magic because the code below compiles 
> even if those are not *required* initializers). Works for NSNumber, 
> NSDecimalNumber, NSString:
> 
>     extension NSNumber : DatabaseValueConvertible {
>         public static func fromDatabaseValue(_ dbValue: DatabaseValue) -> 
> Self? {
>             switch dbValue.storage {
>             case .int64(let int64):
>                 return self.init(value: int64)
>             case .double(let double):
>                 return self.init(value: double)
>             default:
>                 return nil
>             }
>         }
>     }
> 
>     extension NSString : DatabaseValueConvertible {
>         public static func fromDatabaseValue(_ dbValue: DatabaseValue) -> 
> Self? {
>             // Use String conformance
>             guard let string = String.fromDatabaseValue(dbValue) else {
>                 return nil
>             }
>             return self.init(string: string)
>         }
>     }
> 
> The magic about Foundation initializers above makes me doubt that this 
> technique is general enough for Decodable to profit from it, though. Yes it 
> runs on Linux, so I'm not even sure if objc runtime is required or not. I'm 
> clueless ???????
> 
> Gwendal Roué
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to