[swift-evolution] Question regarding SE-0167 Swift Encoders

Gwendal Roué gwendal.roue at gmail.com
Wed May 31 07:36:47 CDT 2017


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.

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

Gwendal
Just a guy that write Swift apps and libraries


> Le 30 mai 2017 à 20:49, Itai Ferber <iferber at apple.com> 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
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170531/21f7a7df/attachment.html>


More information about the swift-evolution mailing list