[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