[swift-evolution] Question regarding SE-0167 Swift Encoders
Itai Ferber
iferber at apple.com
Tue May 30 13:49:05 CDT 2017
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:
```swift
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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170530/626fbc6b/attachment.html>
More information about the swift-evolution
mailing list