<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/xhtml; charset=utf-8">
</head>
<body>
<div style="font-family:sans-serif"><div style="white-space:normal">
<p dir="auto">Hi Gwendal,</p>
<p dir="auto">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>
I have to confess that I’m not familiar with this concept, but let’s take a look:</p>
<pre style="background-color:#F7F7F7; border-radius:5px 5px 5px 5px; margin-left:15px; margin-right:15px; max-width:90vw; overflow-x:auto; padding:5px; color:black" bgcolor="#F7F7F7"><code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0" bgcolor="#F7F7F7"><span style="color: #008800; font-weight: bold">if</span> <span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">valueType</span> = T.<span style="color: #008800; font-weight: bold">self</span> <span style="color: #008800; font-weight: bold">as</span>? DatabaseValueConvertible.<span style="color: #008800; font-weight: bold">Type</span> {
<span style="color: #888888">// if column is missing, trigger the "missing key" error or return nil.</span>
} <span style="color: #008800; font-weight: bold">else</span> <span style="color: #008800; font-weight: bold">if</span> <span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">complexType</span> = T.<span style="color: #008800; font-weight: bold">self</span> <span style="color: #008800; font-weight: bold">as</span>? RowConvertible.<span style="color: #008800; font-weight: bold">Type</span> {
<span style="color: #888888">// if row scope is missing, trigger the "missing key" error or return nil.</span>
} <span style="color: #008800; font-weight: bold">else</span> {
<span style="color: #888888">// don't know what to do</span>
<span style="color: #007020">fatalError</span>(<span style="background-color: #fff0f0">"unsupported"</span>)
}
</code></pre>
<p dir="auto">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">DatabaseValueConvertible</code> nor <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">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">preconditionFailure</code> or an error of some sort, right? In this case, that would be valid.</p>
<p dir="auto">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>
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">— Itai</p>
<p dir="auto">On 29 May 2017, at 4:51, Gwendal Roué via swift-evolution wrote:</p>
</div>
<div style="white-space:normal"><blockquote style="border-left:2px solid #777; color:#777; margin:0 0 5px; padding-left:5px"><p dir="auto">Hello,<br>
<br>
I have already asked stupid questions about SE-0167 and SE-0166, but this time I hope this is a real one.<br>
<br>
According so SE-0166, codable types themselves instantiate a single value decoder, or a keyed container:<br>
<br>
        public struct Farm : Codable {<br>
                public init(from decoder: Decoder) throws {<br>
                        let container = try decoder.container(keyedBy: CodingKeys.self<br>
                        ...<br>
                }<br>
        }<br>
<br>
        public enum Animal : Int, Codable {<br>
                public init(from decoder: Decoder) throws<br>
                 let intValue = try decoder.singleValueContainer().decode(Int.self)<br>
                        ...<br>
                }<br>
        }<br>
        <br>
According to SE-0167, decoder decode non-trivial types in their decode(_:forKey:) and decodeIfPresent(_:forKey:) methods:<br>
<br>
        func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable<br>
        func decodeIfPresent<T>(_ type: T.Type, forKey key: Key) throws -> T? where T : Decodable<br>
<br>
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>
<br>
Why is it a problem?<br>
<br>
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>
<br>
- for values, keys are column names, as everybody can expect<br>
- 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>
<br>
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>
<br>
        struct Compound : Codable {<br>
                let someStruct: SomeStruct // object that feeds on the "someStruct" scope<br>
                let name: String // value that feeds on the "name" column<br>
        }<br>
<br>
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>
<br>
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>
<br>
        if let valueType = T.self as? DatabaseValueConvertible.Type {<br>
                // if column is missing, trigger the "missing key" error or return nil.<br>
        } else if let complexType = T.self as? RowConvertible.Type {<br>
                // if row scope is missing, trigger the "missing key" error or return nil.<br>
        } else {<br>
                // don't know what to do<br>
                fatalError("unsupported")<br>
        }<br>
<br>
Do you have any advice?<br>
<br>
Gwendal Roué<br>
<br>
<br>
_______________________________________________<br>
swift-evolution mailing list<br>
swift-evolution@swift.org<br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" style="color:#777">https://lists.swift.org/mailman/listinfo/swift-evolution</a></p>
</blockquote></div>
<div style="white-space:normal">
</div>
</div>
</body>
</html>