> On 31 May 2017, at 14:36, Gwendal Roué via swift-evolution > <[email protected]> wrote: > > Itai, > > (This email is not technical) > > I'm not claiming that SE-0166 should be able to address all archival formats. > I've been talking about GRDB to show at least one format that SE-0166 doesn't > cover well. And should SE-0166 be fixed to support SQL (in the GRDB fashion), > this does not mean that other developers won't eventually fight with SE-0166 > until they understand it does not fit their bill. > > But there's something very special with SE-0166: > > It's in the standard library, with all the backward-compatibility constraints > that come with such a position. > > IT'S BLESSED WITH CODE GENERATION.
One important future goal I hope we can address in future versions of Swift is powerful macro/meta-programming features. I think like Sourcery (https://github.com/krzysztofzablocki/Sourcery <https://github.com/krzysztofzablocki/Sourcery>) but at compile-time, in the language. Once we have that, I hope we can re-implement SE-0166’s code generation using those meta-programming features into the Standard Library and tear it out of the compiler. If we can achieve that, we will have a strong toolbox for any third-library which have similar needs. PS: With meta-programming, we could also re-implement the automatic Equatable/Hashable conformances. More magic in the compiler is usually a bad thing I think. > I don't know if you, Michael LeHew, Tony Parker, and the core team, realize > the importance of this insanely great privilege granted to this proposal. > > The lack of introspection and macros in Swift makes SE-0166 immensely > attractive for a whole category of libraries. > > When SE-0166 is lacking, should those libs ignore it, and lose CODE > GENERATION, which means looking like it's still Swift 3? > > Should those libs claim SE-0166 conformance, and raise runtime errors for > invalid inputs (where "invalid" does not mean "invalid data", or "invalid > code", but "impossible to fit in SE-0166" <=> "invalid library")? > > I'd like to hear a little better than that :-) GRDB is a library of unusual > quality (sorry for the auto-congratulation). Until now, fatal errors thrown > by GRDB were always a sign of programmer mistake. Not of defects in the > foundations. I wish this would remain true. > > Less caveats and runtime/fatal errors mean less user frustration. > > Less caveats also mean less documentation to write. Ideally, this should be > enough: https://github.com/groue/GRDB.swift/tree/Swift4#codable-records > <https://github.com/groue/GRDB.swift/tree/Swift4#codable-records> > > Gwendal > Just a guy that write Swift apps and libraries > > >> Le 30 mai 2017 à 20:49, Itai Ferber <[email protected] >> <mailto:[email protected]>> a écrit : >> >> Hi Gwendal, >> >> There are no stupid questions — everything helps hammer out this API, so I >> appreciate you taking the time to look at this so deeply. >> I have to confess that I’m not familiar with this concept, but let’s take a >> look: >> >> if let valueType = T.self as? DatabaseValueConvertible.Type { >> // if column is missing, trigger the "missing key" error or return nil. >> } else if let complexType = T.self as? RowConvertible.Type { >> // if row scope is missing, trigger the "missing key" error or return >> nil. >> } else { >> // don't know what to do >> fatalError("unsupported") >> } >> Is it appropriate for a type which is neither DatabaseValueConvertible nor >> RowConvertible to be decoded with your decoder? If not, then this warrants a >> preconditionFailure or an error of some sort, right? In this case, that >> would be valid. >> >> You also mention that "it’s still impossible to support other Codable types" >> — what do you mean by this? Perhaps there’s a way to accomplish what you’re >> looking to do. >> In any case, one option (which is not recommended unless if there are other >> avenues to solve this by) is to perform a "dry run" decoding. Attempt to >> decode the type with a dummy decoder to see what container it will need, >> then prepare your approach and do it again for real. Obviously, this isn’t a >> clean way to do it if we can find alternatives, but it’s an option. >> >> — Itai >> >> On 29 May 2017, at 4:51, Gwendal Roué via swift-evolution wrote: >> >> Hello, >> >> I have already asked stupid questions about SE-0167 and SE-0166, but this >> time I hope this is a real one. >> >> According so SE-0166, codable types themselves instantiate a single value >> decoder, or a keyed container: >> >> public struct Farm : Codable { >> public init(from decoder: Decoder) throws { >> let container = try decoder.container(keyedBy: CodingKeys.self >> ... >> } >> } >> >> public enum Animal : Int, Codable { >> public init(from decoder: Decoder) throws >> let intValue = try decoder.singleValueContainer().decode(Int.self) >> ... >> } >> } >> >> According to SE-0167, decoder decode non-trivial types in their >> decode(_:forKey:) and decodeIfPresent(_:forKey:) methods: >> >> func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T : >> Decodable >> func decodeIfPresent<T>(_ type: T.Type, forKey key: Key) throws -> T? where >> T : Decodable >> >> My trouble is that the decoder does not know whether the Decodable type will >> ask for a keyed container, or for a single value container. >> >> Why is it a problem? >> >> In the context of decoding of SQL rows, keys may refer to different things, >> depending on whether we are decoding a *value*, or a *complex object*: >> >> - for values, keys are column names, as everybody can expect >> - for complex objects, keys are names of "row scopes". Row scopes are a >> concept introduced by GRDB.swift and allows a type that knows how to consume >> `SELECT * FROM table1` to consume as well the results of `SELECT table1.*, >> table2.* FROM table1 JOIN table2` through a "scope" that presents the row in >> the shape expected by the consumer (here, only columns from table1). >> >> This is supposed to allow support for types that contain both nested types >> and values (one of the goals of SE-0166 and SE-0167): >> >> struct Compound : Codable { >> let someStruct: SomeStruct // object that feeds on the "someStruct" scope >> let name: String // value that feeds on the "name" column >> } >> >> The two decoding methods decode(_:forKey:) and decodeIfPresent(_:forKey:) >> can't be implemented nicely, because they don't know whether the decodable >> type will ask for a keyed container or a single value container, and thus >> they don't know whether they should look for the presence of a row scope, or >> of a column: >> >> A workaround is to perform runtime checks on the GRDB protocols adopted by >> T, as below. But it's still impossible to support other codable types: >> >> if let valueType = T.self as? DatabaseValueConvertible.Type { >> // if column is missing, trigger the "missing key" error or return nil. >> } else if let complexType = T.self as? RowConvertible.Type { >> // if row scope is missing, trigger the "missing key" error or return nil. >> } else { >> // don't know what to do >> fatalError("unsupported") >> } >> >> Do you have any advice? >> >> Gwendal Roué >> >> >> _______________________________________________ >> swift-evolution mailing list >> [email protected] <mailto:[email protected]> >> https://lists.swift.org/mailman/listinfo/swift-evolution >> <https://lists.swift.org/mailman/listinfo/swift-evolution> > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
