<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On 31 May 2017, at 14:36, Gwendal Roué via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Itai,</div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">(This email is not technical)</div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">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.</div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">But there's something very special with SE-0166:</div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">It's in the standard library, with all the backward-compatibility constraints that come with such a position.</div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">IT'S BLESSED WITH CODE GENERATION.</div></div></div></blockquote><div><br class=""></div><div>One important future goal I hope we can address in future versions of Swift is powerful macro/meta-programming features. I think like Sourcery (<a href="https://github.com/krzysztofzablocki/Sourcery" class="">https://github.com/krzysztofzablocki/Sourcery</a>) 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.</div><div><br class=""></div><div>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.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">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.</div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">The lack of introspection and macros in Swift makes SE-0166 immensely attractive for a whole category of libraries.</div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">When SE-0166 is lacking, should those libs ignore it, and lose CODE GENERATION, which means looking like it's still Swift 3?</div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">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" &lt;=&gt; "invalid library")?</div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">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.</div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Less caveats and runtime/fatal errors mean less user frustration.</div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Less caveats also mean less documentation to write. Ideally, this should be enough: <a href="https://github.com/groue/GRDB.swift/tree/Swift4#codable-records" class="">https://github.com/groue/GRDB.swift/tree/Swift4#codable-records</a></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""></div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Gwendal</div><div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Just a guy that write Swift apps and libraries</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="">Le 30 mai 2017 à 20:49, Itai Ferber &lt;<a href="mailto:iferber@apple.com" class="">iferber@apple.com</a>&gt; a écrit :</div><br class="Apple-interchange-newline"><div class="">


<meta http-equiv="Content-Type" content="text/xhtml; charset=utf-8" class="">

<div class="">
<div style="font-family:sans-serif" class=""><div style="white-space:normal" class=""><p dir="auto" class="">Hi Gwendal,</p><p dir="auto" class="">There are no stupid questions — everything helps hammer out this API, so I appreciate you taking the time to look at this so deeply.<br class="">
I have to confess that I’m not familiar with this concept, but let’s take a look:</p>

<pre style="background-color: rgb(247, 247, 247); border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; margin-left: 15px; margin-right: 15px; max-width: 90vw; overflow-x: auto; padding: 5px;" bgcolor="#F7F7F7" class=""><code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0" bgcolor="#F7F7F7" class=""><span style="color: #008800; font-weight: bold" class="">if</span> <span style="color: #008800; font-weight: bold" class="">let</span> <span style="color: #996633" class="">valueType</span> = T.<span style="color: #008800; font-weight: bold" class="">self</span> <span style="color: #008800; font-weight: bold" class="">as</span>? DatabaseValueConvertible.<span style="color: #008800; font-weight: bold" class="">Type</span> {
    <span style="color: #888888" class="">// if column is missing, trigger the "missing key" error or return nil.</span>
} <span style="color: #008800; font-weight: bold" class="">else</span> <span style="color: #008800; font-weight: bold" class="">if</span> <span style="color: #008800; font-weight: bold" class="">let</span> <span style="color: #996633" class="">complexType</span> = T.<span style="color: #008800; font-weight: bold" class="">self</span> <span style="color: #008800; font-weight: bold" class="">as</span>? RowConvertible.<span style="color: #008800; font-weight: bold" class="">Type</span> {
    <span style="color: #888888" class="">// if row scope is missing, trigger the "missing key" error or return nil.</span>
} <span style="color: #008800; font-weight: bold" class="">else</span> {
    <span style="color: #888888" class="">// don't know what to do</span>
    <span style="color: #007020" class="">fatalError</span>(<span style="background-color: #fff0f0" class="">"unsupported"</span>)
}
</code></pre><p dir="auto" class="">Is it appropriate for a type which is neither <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7" class="">DatabaseValueConvertible</code> nor <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7" class="">RowConvertible</code> to be decoded with your decoder? If not, then this warrants a <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7" class="">preconditionFailure</code> or an error of some sort, right? In this case, that would be valid.</p><p dir="auto" class="">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.<br class="">
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.</p><p dir="auto" class="">— Itai</p><p dir="auto" class="">On 29 May 2017, at 4:51, Gwendal Roué via swift-evolution wrote:</p>

</div>
<div style="white-space:normal" class=""><blockquote style="border-left:2px solid #777; color:#777; margin:0 0 5px; padding-left:5px" class=""><p dir="auto" class="">Hello,<br class="">
<br class="">
I have already asked stupid questions about SE-0167 and SE-0166, but this time I hope this is a real one.<br class="">
<br class="">
According so SE-0166, codable types themselves instantiate a single value decoder, or a keyed container:<br class="">
<br class="">
        public struct Farm : Codable {<br class="">
                public init(from decoder: Decoder) throws {<br class="">
                        let container = try decoder.container(keyedBy: CodingKeys.self<br class="">
                        ...<br class="">
                }<br class="">
        }<br class="">
<br class="">
        public enum Animal : Int, Codable {<br class="">
                public init(from decoder: Decoder) throws<br class="">
                        let intValue = try decoder.singleValueContainer().decode(Int.self)<br class="">
                        ...<br class="">
                }<br class="">
        }<br class="">
        <br class="">
According to SE-0167, decoder decode non-trivial types in their decode(_:forKey:) and decodeIfPresent(_:forKey:) methods:<br class="">
<br class="">
        func decode&lt;T&gt;(_ type: T.Type, forKey key: Key) throws -&gt; T where T : Decodable<br class="">
        func decodeIfPresent&lt;T&gt;(_ type: T.Type, forKey key: Key) throws -&gt; T? where T : Decodable<br class="">
<br class="">
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.<br class="">
<br class="">
Why is it a problem?<br class="">
<br class="">
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*:<br class="">
<br class="">
- for values, keys are column names, as everybody can expect<br class="">
- 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).<br class="">
<br class="">
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):<br class="">
<br class="">
        struct Compound : Codable {<br class="">
                let someStruct: SomeStruct // object that feeds on the "someStruct" scope<br class="">
                let name: String // value that feeds on the "name" column<br class="">
        }<br class="">
<br class="">
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:<br class="">
<br class="">
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:<br class="">
<br class="">
        if let valueType = T.self as? DatabaseValueConvertible.Type {<br class="">
                // if column is missing, trigger the "missing key" error or return nil.<br class="">
        } else if let complexType = T.self as? RowConvertible.Type {<br class="">
                // if row scope is missing, trigger the "missing key" error or return nil.<br class="">
        } else {<br class="">
                // don't know what to do<br class="">
                fatalError("unsupported")<br class="">
        }<br class="">
<br class="">
Do you have any advice?<br class="">
<br class="">
Gwendal Roué<br class="">
<br class="">
<br class="">
_______________________________________________<br class="">
swift-evolution mailing list<br class="">
<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" style="color:#777" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a></p>
</blockquote></div>
<div style="white-space:normal" class="">
</div>
</div>
</div>

</div></blockquote></div><br class=""></div>_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></body></html>