<!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">Thanks for the thorough and detailed review, Brent! Responses inline.</p>
<p dir="auto">On 15 Mar 2017, at 21:19, Brent Royal-Gordon wrote:</p>
<p dir="auto"></p></div>
<div style="white-space:normal"><blockquote style="border-left:2px solid #777; color:#777; margin:0 0 5px; padding-left:5px"><blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto">On Mar 15, 2017, at 3:40 PM, Itai Ferber via swift-evolution <swift-evolution@swift.org> wrote:<br>
<br>
Hi everyone,<br>
<br>
The following introduces a new Swift-focused archival and serialization API as part of the Foundation framework. We’re interested in improving the experience and safety of performing archival and serialization, and are happy to receive community feedback on this work.</p>
</blockquote><p dir="auto">Thanks to all of the people who've worked on this. It's a great proposal.<br>
</p>
<blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto">Specifically:<br>
<br>
        • It aims to provide a solution for the archival of Swift struct and enum types</p>
</blockquote><p dir="auto">I see a lot of discussion here of structs and classes, and an example of an enum without associated values, but I don't see any discussion of enums with associated values. Can you sketch how you see people encoding such types?<br>
<br>
For example, I assume that `Optional` is going to get some special treatment, but if it doesn't, how would you write its `encode(to:)` method?</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto"><code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Optional</code> values are accepted and vended directly through the API. The <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">encode(_:forKey:)</code> methods take optional values directly, and <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">decodeIfPresent(_:forKey:)</code> vend optional values.</p>
<p dir="auto"><code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Optional</code> is special in this way — it’s a primitive part of the system. It’s actually not possible to write an <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">encode(to:)</code> method for <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Optional</code>, since the representation of null values is up to the encoder and the format it’s working in; <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">JSONEncoder</code>, for instance, decides on the representation of <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">nil</code> (JSON <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">null</code>). It wouldn’t be possible to ask <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">nil</code> to encode itself in a reasonable way.</p>
<p dir="auto"></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">What about a more complex enum, like the standard library's `UnicodeDecodingResult`:<br>
<br>
        enum UnicodeDecodingResult {<br>
                case emptyInput<br>
                case error<br>
                case scalarValue(UnicodeScalar)<br>
        }<br>
<br>
Or, say, an `Error`-conforming type from one of my projects:<br>
<br>
        public enum SQLError: Error {<br>
         case connectionFailed(underlying: Error)<br>
         case executionFailed(underlying: Error, statement: SQLStatement)<br>
         case noRecordsFound(statement: SQLStatement)<br>
         case extraRecordsFound(statement: SQLStatement)<br>
         case columnInvalid(underlying: Error, key: ColumnSpecifier, statement: SQLStatement)<br>
         case valueInvalid(underlying: Error, key: AnySQLColumnKey, statement: SQLStatement)<br>
        }<br>
<br>
(You can assume that all the types in the associated values are `Codable`.)</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">Sure — these cases specifically do not derive <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Codable</code> conformance because the specific representation to choose is up to you. Two possible ways to write this, though there are many others (I’m simplifying these cases here a bit, but you can extrapolate this):</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: #888888">// Approach 1</span>
<span style="color: #888888">// This produces either {"type": 0} for `.noValue`, or {"type": 1, "value": …} for `.associated`.</span>
<span style="color: #008800; font-weight: bold">public</span> <span style="color: #008800; font-weight: bold">enum</span> <span style="color: #BB0066; font-weight: bold">EnumWithAssociatedValue</span> : Codable {
<span style="color: #008800; font-weight: bold">case</span> noValue
<span style="color: #008800; font-weight: bold">case</span> associated(<span style="color: #007020">Int</span>)
<span style="color: #008800; font-weight: bold">private</span> <span style="color: #008800; font-weight: bold">enum</span> <span style="color: #BB0066; font-weight: bold">CodingKeys</span> : CodingKey {
<span style="color: #008800; font-weight: bold">case</span> type
<span style="color: #008800; font-weight: bold">case</span> value
}
<span style="color: #008800; font-weight: bold">public</span> <span style="color: #008800; font-weight: bold">init</span>(from decoder: Decoder) throws {
<span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">container</span> = try decoder.container(keyedBy: CodingKeys.<span style="color: #008800; font-weight: bold">self</span>)
<span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">type</span> = try container.decode(<span style="color: #007020">Int</span>.<span style="color: #008800; font-weight: bold">self</span>, forKey: .type)
<span style="color: #008800; font-weight: bold">switch</span> type {
<span style="color: #008800; font-weight: bold">case</span> <span style="color: #0000DD; font-weight: bold">0</span>:
<span style="color: #008800; font-weight: bold">self</span> = .noValue
<span style="color: #008800; font-weight: bold">case</span> <span style="color: #0000DD; font-weight: bold">1</span>:
<span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">value</span> = try container.decode(<span style="color: #007020">Int</span>.<span style="color: #008800; font-weight: bold">self</span>, forKey: .value)
<span style="color: #008800; font-weight: bold">self</span> = .associated(value)
<span style="color: #008800; font-weight: bold">default</span>:
throw <span style="color: #FF0000; background-color: #FFAAAA">…</span>
}
}
<span style="color: #008800; font-weight: bold">public</span> <span style="color: #008800; font-weight: bold">func</span> <span style="color: #0066BB; font-weight: bold">encode</span>(to encoder: Encoder) throws {
<span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">container</span> = encoder.container(keyedBy: codingKeys.<span style="color: #008800; font-weight: bold">self</span>)
<span style="color: #008800; font-weight: bold">switch</span> <span style="color: #008800; font-weight: bold">self</span> {
<span style="color: #008800; font-weight: bold">case</span> .noValue:
try container.encode(<span style="color: #0000DD; font-weight: bold">0</span>, forKey: .type)
<span style="color: #008800; font-weight: bold">case</span> .associated(<span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">value</span>):
try container.encode(<span style="color: #0000DD; font-weight: bold">1</span>, forKey: .type)
try container.encode(value, forKey: .value)
}
}
}
<span style="color: #888888">// Approach 2</span>
<span style="color: #888888">// Produces `0`, `1`, or `2` for `.noValue1`, `.noValue2`, and `.noValue3` respectively.</span>
<span style="color: #888888">// Produces {"type": 3, "value": …} and {"type": 4, "value": …} for `.associated1` and `.associated2`.</span>
<span style="color: #008800; font-weight: bold">public</span> <span style="color: #008800; font-weight: bold">enum</span> <span style="color: #BB0066; font-weight: bold">EnumWithAssociatedValue</span> : Codable {
<span style="color: #008800; font-weight: bold">case</span> noValue1
<span style="color: #008800; font-weight: bold">case</span> noValue2
<span style="color: #008800; font-weight: bold">case</span> noValue3
<span style="color: #008800; font-weight: bold">case</span> associated1(<span style="color: #007020">Int</span>)
<span style="color: #008800; font-weight: bold">case</span> associated2(<span style="color: #007020">String</span>)
<span style="color: #008800; font-weight: bold">private</span> <span style="color: #008800; font-weight: bold">enum</span> <span style="color: #BB0066; font-weight: bold">CodingKeys</span> : CodingKey {
<span style="color: #008800; font-weight: bold">case</span> type
<span style="color: #008800; font-weight: bold">case</span> value
}
<span style="color: #008800; font-weight: bold">public</span> <span style="color: #008800; font-weight: bold">init</span>(from decoder: Decoder) throws {
<span style="color: #008800; font-weight: bold">if</span> <span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">container</span> = try? decoder.singleValueContainer() {}
<span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">type</span> = container.decode(<span style="color: #007020">Int</span>.<span style="color: #008800; font-weight: bold">self</span>)
<span style="color: #008800; font-weight: bold">switch</span> type {
<span style="color: #008800; font-weight: bold">case</span> <span style="color: #0000DD; font-weight: bold">0</span>: <span style="color: #008800; font-weight: bold">self</span> = .noValue1
<span style="color: #008800; font-weight: bold">case</span> <span style="color: #0000DD; font-weight: bold">1</span>: <span style="color: #008800; font-weight: bold">self</span> = .noValue2
<span style="color: #008800; font-weight: bold">case</span> <span style="color: #0000DD; font-weight: bold">2</span>: <span style="color: #008800; font-weight: bold">self</span> = .noValue3
<span style="color: #008800; font-weight: bold">default</span>: throw <span style="color: #FF0000; background-color: #FFAAAA">…</span>
}
} <span style="color: #008800; font-weight: bold">else</span> {
<span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">container</span> = try decoder.container(keyedBy: CodingKeys.<span style="color: #008800; font-weight: bold">self</span>)
<span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">type</span> = container.decode(<span style="color: #007020">Int</span>.<span style="color: #008800; font-weight: bold">self</span>, forKey: .type)
<span style="color: #008800; font-weight: bold">switch</span> type {
<span style="color: #008800; font-weight: bold">case</span> <span style="color: #0000DD; font-weight: bold">3</span>:
<span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">value</span> = container.decode(<span style="color: #007020">Int</span>.<span style="color: #008800; font-weight: bold">self</span>, forKey: .value)
<span style="color: #008800; font-weight: bold">self</span> = .associated1(value)
<span style="color: #008800; font-weight: bold">case</span> <span style="color: #0000DD; font-weight: bold">4</span>:
<span style="color: #008800; font-weight: bold">let</span> <span style="color: #996633">value</span> = container.decode(<span style="color: #007020">String</span>.<span style="color: #008800; font-weight: bold">self</span>, forKey: .value)
<span style="color: #008800; font-weight: bold">self</span> = .associated2(value)
<span style="color: #008800; font-weight: bold">default</span>: throw ...
}
}
}
}
</code></pre>
<p dir="auto">There are, of course, many more approaches that you could take, but these are just two examples. The first is likely simpler to read and comprehend, but may not be appropriate if you’re trying to optimize for space.</p>
<p dir="auto"></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">I don't necessarily assume that the compiler should write conformances to these sorts of complicated enums for me (though that would be nice!); I'm just wondering what the designers of this feature envision people doing in cases like these.<br>
</p>
<blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto">        • protocol Codable: Adopted by types to opt into archival. Conformance may be automatically derived in cases where all properties are also Codable.</p>
</blockquote><p dir="auto">Have you given any consideration to supporting types which only need to decode? That seems likely to be common when interacting with web services.</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">We have. Ultimately, we decided that the introduction of several protocols to cover encodability, decodability, and both was too much of a cognitive overhead, considering the number of other types we’re also introducing. You can always implement <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">encode(to:)</code> as <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">fatalError()</code>.</p>
<p dir="auto"></p></div>
<div style="white-space:normal"><blockquote style="border-left:2px solid #777; color:#777; margin:0 0 5px; padding-left:5px"><blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto">        • protocol CodingKey: Adopted by types used as keys for keyed containers, replacing String keys with semantic types. Conformance may be automatically derived in most cases.<br>
        • protocol Encoder: Adopted by types which can take Codable values and encode them into a native format.<br>
                • class KeyedEncodingContainer<Key : CodingKey>: Subclasses of this type provide a concrete way to store encoded values by CodingKey. Types adopting Encoder should provide subclasses of KeyedEncodingContainer to vend.<br>
                • protocol SingleValueEncodingContainer: Adopted by types which provide a concrete way to store a single encoded value. Types adopting Encoder should provide types conforming to SingleValueEncodingContainer to vend (but in many cases will be able to conform to it themselves).<br>
        • protocol Decoder: Adopted by types which can take payloads in a native format and decode Codable values out of them.<br>
                • class KeyedDecodingContainer<Key : CodingKey>: Subclasses of this type provide a concrete way to retrieve encoded values from storage by CodingKey. Types adopting Decoder should provide subclasses of KeyedDecodingContainer to vend.<br>
                • protocol SingleValueDecodingContainer: Adopted by types which provide a concrete way to retrieve a single encoded value from storage. Types adopting Decoder should provide types conforming to SingleValueDecodingContainer to vend (but in many cases will be able to conform to it themselves).</p>
</blockquote><p dir="auto">I do want to note that, at this point in the proposal, I was sort of thinking you'd gone off the deep end modeling this. Having read the whole thing, I now understand what all of these things do, but this really is a very large subsystem. I think it's worth asking if some of these types can be eliminated or combined.</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">In the past, the concepts of <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">SingleValueContainer</code> and <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code> were not distinct — all of the methods on <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">SingleValueContainer</code> were just part of <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code>. Sure, this is a simpler system, but unfortunately promotes the wrong thing altogether. I’ll address this below.</p>
<p dir="auto"></p></div>
<div style="white-space:normal"><blockquote style="border-left:2px solid #777; color:#777; margin:0 0 5px; padding-left:5px"><blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto">Structured types (i.e. types which encode as a collection of properties) encode and decode their properties in a keyed manner. Keys may be String-convertible or Int-convertible (or both),</p>
</blockquote><p dir="auto">What does "may" mean here? That, at runtime, the encoder will test for the preferred key type and fall back to the other one? That seems a little bit problematic.</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">Yes, this is the case. A lot is left up to the <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code> because it can choose to do something for its format that your implementation of <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">encode(to:)</code> may not have considered.<br>
If you try to encode something with an <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Int</code> key in a string-keyed dictionary, the encoder may choose to stringify the integer if appropriate for the format. If not, it can reject your key, ignore the call altogether, <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">preconditionFailure()</code>, etc. It is also perfectly legitimate to write an <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code> which supports a flat encoding format — in that case, keys are likely ignored altogether, in which case there is no error to be had. We’d like to not arbitrarily constrain an implementation unless necessary.</p>
<p dir="auto">FWIW, 99.9% of the time, the appropriate thing to do is to either simply throw an error, or <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">preconditionFailure()</code>. Nasal demons should not be the common case. But for some encoding formats, this is appropriate.</p>
<p dir="auto"></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">I'm also quite worried about how `Int`-convertible keys will interact with code synthesis. The obvious way to assign integers—declaration order—would mean that reordering declarations would invisibly break archiving, potentially (if the types were compatible) without breaking anything in an error-causing way even at runtime. You could sort the names, but then adding a new property would shift the integers of the properties "below" it. You could hash the names, but then there's no obvious relationship between the integers and key cases.<br>
<br>
At the same time, I also think that using arbitrary integers is a poor match for ordering. If you're making an ordered container, you don't want arbitrary integers wrapped up in an abstract type. You want adjacent integers forming indices of an eventual array. (Actually, you may not want indices at all—you may just want to feed elements in one at a time!)</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">For these exact reasons, integer keys are not produced by code synthesis, only string keys. If you want integer keys, you’ll have to write them yourself. :)</p>
<p dir="auto">Integer keys are fragile, as you point out yourself, and while we’d like to encourage their use as appropriate, they require explicit thought and care as to their use.</p>
<p dir="auto"></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">So I would suggest the following changes:<br>
<br>
* The coding key always converts to a string. That means we can eliminate the `CodingKey` protocol and instead use `RawRepresentable where RawValue == String`, leveraging existing infrastructure. That also means we can call the `CodingKeys` associated type `CodingKey` instead, which is the correct name for it—we're not talking about an `OptionSet` here.<br>
<br>
* If, to save space on disk, you want to also people to use integers as the serialized representation of a key, we might introduce a parallel `IntegerCodingKey` protocol for that, but every `CodingKey` type should map to `String` first and foremost. Using a protocol here ensures that it can be statically determined at compile time whether a type can be encoded with integer keys, so the compiler can select an overload of `container(keyedBy:)`.<br>
<br>
* Intrinsically ordered data is encoded as a single value containers of type `Array<Codable>`. (I considered having an `orderedContainer()` method and type, but as I thought about it, I couldn't think of an advantage it would have over `Array`.)</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">This is possible, but I don’t see this as necessarily advantageous over what we currently have. In 99.9% of cases, <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">CodingKey</code> types will have string values anyway — in many cases you won’t have to write the <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">enum</code> yourself to begin with, but even when you do, derived <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">CodingKey</code> conformance will generate string values on your behalf.<br>
The only time a key will not have a string value is if the <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">CodingKey</code> protocol is implemented manually and a value is either deliberately left out, or there was a mistake in the implementation; in either case, there wouldn’t have been a valid string value anyway.</p>
<p dir="auto"></p></div>
<div style="white-space:normal"><blockquote style="border-left:2px solid #777; color:#777; margin:0 0 5px; padding-left:5px"><blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto"> /// Returns an encoding container appropriate for holding a single primitive value.<br>
///<br>
/// - returns: A new empty single value container.<br>
/// - precondition: May not be called after a prior `self.container(keyedBy:)` call.<br>
/// - precondition: May not be called after a value has been encoded through a previous `self.singleValueContainer()` call.<br>
func singleValueContainer() -> SingleValueEncodingContainer</p>
</blockquote><p dir="auto">Speaking of which, I'm not sure about single value containers. My first instinct is to say that methods should be moved from them to the `Encoder` directly, but that would probably cause code duplication. But...isn't there already duplication between the `SingleValue*Container` and the `Keyed*Container`? Why, yes, yes there is. So let's talk about that.</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">In the Alternatives Considered section of the proposal, we detail having done just this. Originally, the requirements now on <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">SingleValueContainer</code> sat on <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code> and <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Decoder</code>.<br>
Unfortunately, this made it too easy to do the wrong thing, and required extra work (in comparison) to do the right thing.</p>
<p dir="auto">When <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code> has <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">encode(_ value: Bool?)</code>, <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">encode(_ value: Int?)</code>, etc. on it, it’s very intuitive to try to encode values that way:</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">func</span> <span style="color: #0066BB; font-weight: bold">encode</span>(to encoder: Encoder) throws {
<span style="color: #888888">// The very first thing I try to type is encoder.enc… and guess what pops up in autocomplete:</span>
try encoder.encode(myName)
try encoder.encode(myEmail)
try encoder.encode(myAddress)
}
</code></pre>
<p dir="auto">This might look right to someone expecting to be able to encode in an ordered fashion, which is <em>not</em> what these methods do.<br>
In addition, for someone expecting keyed encoding methods, this is very confusing. Where are those methods? Where don’t these "default" methods have keys?</p>
<p dir="auto">The very first time that code block ran, it would <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">preconditionFailure()</code> or throw an error, since those methods intend to encode only one single value.</p>
<p dir="auto"></p></div>
<div style="white-space:normal"><blockquote style="border-left:2px solid #777; color:#777; margin:0 0 5px; padding-left:5px"><blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto"> open func encode<Value : Codable>(_ value: Value?, forKey key: Key) throws<br>
open func encode(_ value: Bool?, forKey key: Key) throws<br>
open func encode(_ value: Int?, forKey key: Key) throws<br>
open func encode(_ value: Int8?, forKey key: Key) throws<br>
open func encode(_ value: Int16?, forKey key: Key) throws<br>
open func encode(_ value: Int32?, forKey key: Key) throws<br>
open func encode(_ value: Int64?, forKey key: Key) throws<br>
open func encode(_ value: UInt?, forKey key: Key) throws<br>
open func encode(_ value: UInt8?, forKey key: Key) throws<br>
open func encode(_ value: UInt16?, forKey key: Key) throws<br>
open func encode(_ value: UInt32?, forKey key: Key) throws<br>
open func encode(_ value: UInt64?, forKey key: Key) throws<br>
open func encode(_ value: Float?, forKey key: Key) throws<br>
open func encode(_ value: Double?, forKey key: Key) throws<br>
open func encode(_ value: String?, forKey key: Key) throws<br>
open func encode(_ value: Data?, forKey key: Key) throws</p>
</blockquote><p dir="auto">Wait, first, a digression for another issue: I'm concerned that, if you look at the `decode` calls, there are plain `decode(…)` calls which throw if a `nil` was originally encoded and `decodeIfPresent` calls which return optional. The result is, essentially, that the encoding system eats a level of optionality for its own purposes—seemingly good, straightforward-looking code like this:<br>
<br>
        struct MyRecord: Codable {<br>
                var id: Int?<br>
                …<br>
                <br>
                func encode(to encoder: Encoder) throws {<br>
                        let container = encoder.container(keyedBy: CodingKey.self)<br>
                        try container.encode(id, forKey: .id)<br>
                        …<br>
                }<br>
                <br>
                init(from decoder: Decoder) throws {<br>
                        let container = decoder.container(keyedBy: CodingKey.self)<br>
                        id = try container.decode(Int.self, forKey: .id)<br>
                        …<br>
                }<br>
        }<br>
<br>
Will crash. (At least, I assume that's what will happen.)</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">The return type of <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">decode(Int.self, forKey: .id)</code> is <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Int</code>. I’m not convinced that it’s possible to misconstrue that as the correct thing to do here. How would that return a <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">nil</code> value if the value was <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">nil</code> to begin with?<br>
The only other method that would be appropriate is <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">decodeIfPresent(Int.self, forKey: .id)</code>, which is exactly what you want.</p>
<p dir="auto"></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">I think we'd be better off having `encode(_:forKey:)` not take an optional; instead, we should have `Optional` conform to `Codable` and behave in some appropriate way. Exactly how to implement it might be a little tricky because of nested optionals; I suppose a `none` would have to measure how many levels of optionality there are between it and a concrete value, and then encode that information into the data. I think our `NSNull` bridging is doing something broadly similar right now.</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto"><code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Optional</code> cannot encode to <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Codable</code> for the reasons given above. It is a primitive type much like <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Int</code> and <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">String</code>, and it’s up to the encoder and the format to represent it.<br>
How would <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Optional</code> encode <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">nil</code>?</p>
<p dir="auto"></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">I know that this is not the design you would use in Objective-C, but Swift uses `Optional` differently from how Objective-C uses `nil`. Swift APIs consider `nil` and absent to be different things; where they can both occur, good Swift APIs use doubled-up Optionals to be precise about the situation. I think the design needs to be a little different to accommodate that.<br>
<br>
Now, back to the `SingleValue*Container`/`Keyed*Container` issue. The list above is, frankly, gigantic. You specify a *lot* of primitives in `Keyed*Container`; there's a lot to implement here. And then you have to implement it all *again* in `SingleValue*Container`:<br>
</p>
<blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto"> func encode(_ value: Bool) throws<br>
func encode(_ value: Int) throws<br>
func encode(_ value: Int8) throws<br>
func encode(_ value: Int16) throws<br>
func encode(_ value: Int32) throws<br>
func encode(_ value: Int64) throws<br>
func encode(_ value: UInt) throws<br>
func encode(_ value: UInt8) throws<br>
func encode(_ value: UInt16) throws<br>
func encode(_ value: UInt32) throws<br>
func encode(_ value: UInt64) throws<br>
func encode(_ value: Float) throws<br>
func encode(_ value: Double) throws<br>
func encode(_ value: String) throws<br>
func encode(_ value: Data) throws</p>
</blockquote><p dir="auto">This is madness.<br>
<br>
Look, here's what we do. You have two types: `Keyed*Container` and `Value*Container`. `Keyed*Container` looks something like this:<br>
<br>
        final public class KeyedEncodingContainer<EncoderType: Encoder, Key: RawRepresentable> where Key.RawValue == String {<br>
         public let encoder: EncoderType<br>
        <br>
         public let codingKeyContext: [RawRepresentable where RawValue == String]<br>
         // Hmm, we might need a CodingKey protocol after all.<br>
         // Still, it could just be `protocol CodingKey: RawRepresentable where RawValue == String {}`<br>
        <br>
         subscript (key: Key) -> ValueEncodingContainer {<br>
         return encoder.makeValueEncodingContainer(forKey: key)<br>
         }<br>
        }<br>
<br>
It's so simple, it doesn't even need to be specialized. You might even be able to get away with combining the encoding and decoding variants if the subscript comes from a conditional extension. `Value*Container` *does* need to be specialized; it looks like this (modulo the `Optional` issue I mentioned above):</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">Sure, let’s go with this for a moment. Presumably, then, <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code> would be able to vend out both <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">KeyedEncodingContainer</code>s and <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">ValueEncodingContainer</code>s, correct?</p>
<p dir="auto"></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">        public protocol ValueEncodingContainer {<br>
         func encode<Value : Codable>(_ value: Value?, forKey key: Key) throws</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">I’m assuming that the key here is a typo, correct?<br>
Keep in mind that combining these concepts changes the semantics of how single-value encoding works. Right now <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">SingleValueEncodingContainer</code> only allows values of primitive types; this would allow you to encode a value in terms of a different arbitrarily-codable value.</p>
<p dir="auto"></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">         func encode(_ value: Bool?) throws<br>
         func encode(_ value: Int?) throws<br>
         func encode(_ value: Int8?) throws<br>
         func encode(_ value: Int16?) throws<br>
         func encode(_ value: Int32?) throws<br>
         func encode(_ value: Int64?) throws<br>
         func encode(_ value: UInt?) throws<br>
         func encode(_ value: UInt8?) throws<br>
         func encode(_ value: UInt16?) throws<br>
         func encode(_ value: UInt32?) throws<br>
         func encode(_ value: UInt64?) throws<br>
         func encode(_ value: Float?) throws<br>
         func encode(_ value: Double?) throws<br>
         func encode(_ value: String?) throws<br>
         func encode(_ value: Data?) throws<br>
        <br>
         func encodeWeak<Object : AnyObject & Codable>(_ object: Object?) throws</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">Same comment here.</p>
<p dir="auto"></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">         var codingKeyContext: [CodingKey]<br>
        }<br>
<br>
And use sites would look like:<br>
<br>
        func encode(to encoder: Encoder) throws {<br>
                let container = encoder.container(keyedBy: CodingKey.self)<br>
                try container[.id].encode(id)<br>
                try container[.name].encode(name)<br>
                try container[.birthDate].encode(birthDate)<br>
        }</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">For consumers, this doesn’t seem to make much of a difference. We’ve turned <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">try container.encode(id, forKey:. id)</code> into <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">try container[.id].encode(id)</code>.</p>
<p dir="auto"></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">Decoding is slightly tricker. You could either make the subscript `Optional`, which would be more like `Dictionary` but would be inconsistent with `Encoder` and would give the "never force-unwrap anything" crowd conniptions, or you could add a `contains()` method to `ValueDecodingContainer` and make `decode(_:)` throw. Either one works.<br>
<br>
Also, another issue with the many primitives: swiftc doesn't really like large overload sets very much. Could this set be reduced? I'm not sure what the logic was in choosing these particular types, but many of them share protocols in Swift—you might get away with just this:<br>
<br>
        public protocol ValueEncodingContainer {<br>
         func encode<Value : Codable>(_ value: Value?, forKey key: Key) throws<br>
         func encode(_ value: Bool?, forKey key: Key) throws<br>
         func encode<Integer: SignedInteger>(_ value: Integer?, forKey key: Key) throws<br>
         func encode<UInteger: UnsignedInteger>(_ value: UInteger?, forKey key: Key) throws<br>
         func encode<Floating: FloatingPoint>(_ value: Floating?, forKey key: Key) throws<br>
         func encode(_ value: String?, forKey key: Key) throws<br>
         func encode(_ value: Data?, forKey key: Key) throws<br>
        <br>
         func encodeWeak<Object : AnyObject & Codable>(_ object: Object?, forKey key: Key) throws<br>
        <br>
         var codingKeyContext: [CodingKey]<br>
        }</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">These types were chosen because we want the API to make static guarantees about concrete types which all <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code>s and <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Decoder</code>s should support. This is somewhat less relevant for JSON, but more relevant for binary formats where the difference between <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Int16</code> and <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Int64</code> is critical.<br>
This turns the concrete type check into a runtime check that <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code> authors need to keep in mind. More so, however, any type can conform to <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">SignedInteger</code> or <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">UnsignedInteger</code> as long as it fulfills the protocol requirements. I can write an <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Int37</code> type, but no encoder could make sense of that type, and that failure is a runtime failure. If you want a concrete example, <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Float80</code> conforms to <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">FloatingPoint</code>; no popular binary format I’ve seen supports 80-bit floats, though — we cannot prevent that call statically…</p>
<p dir="auto">Instead, we want to offer a static, concrete list of types that <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code>s and <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Decoder</code>s must be aware of, and that consumers have guarantees about support for.</p>
<p dir="auto"></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">To accommodate my previous suggestion of using arrays to represent ordered encoded data, I would add one more primitive:<br>
<br>
         func encode(_ values: [Codable]) throws</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">Collection types are purposefully not primitives here:</p>
<ul>
<li>If <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Array</code> is a primitive, but does not conform to <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Codable</code>, then you cannot encode <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Array<Array<Codable>></code>.</li>
<li>If <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Array</code> is a primitive, and conforms to <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Codable</code>, then there may be ambiguity between <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">encode(_ values: [Codable])</code> and <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">encode(_ value: Codable)</code>.
<ul>
<li>Even in cases where there are not, inside of <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">encode(_ values: [Codable])</code>, if I call <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">encode([[1,2],[3,4]])</code>, you’ve lost type information about what’s contained in the array — all you see is <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Codable</code></li>
<li>If you change it to <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">encode<Value : Codable>(_ values: [Value])</code> to compensate for that, you still cannot infinitely recurse on what type <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Value</code> is. Try it with <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">encode([[[[1]]]])</code> and you’ll see what I mean; at some point the inner types are no longer preserved.</li>
</ul></li>
</ul>
<p dir="auto"></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">(Also, is there any sense in adding `Date` to this set, since it needs special treatment in many of our formats?)</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">We’ve considered adding <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Date</code> to this list. However, this means that any format that is a part of this system needs to be able to make a decision about how to format dates. Many binary formats have no native representations of dates, so this is not necessarily a guarantee that all formats can make.</p>
<p dir="auto">Looking for additional opinions on this one.</p>
<p dir="auto"></p></div>
<div style="white-space:normal"><blockquote style="border-left:2px solid #777; color:#777; margin:0 0 5px; padding-left:5px"><blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto">Encoding Container Types<br>
<br>
For some types, the container into which they encode has meaning. Especially when coding for a specific output format (e.g. when communicating with a JSON API), a type may wish to explicitly encode as an array or a dictionary:<br>
<br>
// Continuing from before<br>
public protocol Encoder {<br>
func container<Key : CodingKey>(keyedBy keyType: Key.Type, type containerType: EncodingContainerType) -> KeyedEncodingContainer<Key><br>
}<br>
<br>
/// An `EncodingContainerType` specifies the type of container an `Encoder` should use to store values.<br>
public enum EncodingContainerType {<br>
/// The `Encoder`'s preferred container type; equivalent to either `.array` or `.dictionary` as appropriate for the encoder.<br>
case `default`<br>
<br>
/// Explicitly requests the use of an array to store encoded values.<br>
case array<br>
<br>
/// Explicitly requests the use of a dictionary to store encoded values.<br>
case dictionary<br>
}</p>
</blockquote><p dir="auto">I see what you're getting at here, but I don't think this is fit for purpose, because arrays are not simply dictionaries with integer keys—their elements are adjacent and ordered. See my discussion earlier about treating inherently ordered containers as simply single-value `Array`s.</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">You’re right in that arrays are not simply dictionaries with integer keys, but I don’t see where we make that assertion here.<br>
If an <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code> is asked for an array and is provided with integer keys, it can use those keys as indices. If the keys are non-contiguous, the intervening spaces can be filled with null values (if appropriate for the format; if not, the operation can error out).</p>
<p dir="auto">The way these containers are handled is completely up to the <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code>. An <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code> producing an array may choose to ignore keys altogether and simply produce an array from the values given to it sequentially. (This is not recommended, but possible.)</p>
<p dir="auto"></p></div>
<div style="white-space:normal"><blockquote style="border-left:2px solid #777; color:#777; margin:0 0 5px; padding-left:5px"><blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto">Nesting<br>
<br>
In practice, some types may also need to control how data is nested within their container, or potentially nest other containers within their container. Keyed containers allow this by returning nested containers of differing key types:</p>
</blockquote><p dir="auto">[snip]<br>
</p>
<blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto">This can be common when coding against specific external data representations:<br>
<br>
// User type for interfacing with a specific JSON API. JSON API expects encoding as {"id": ..., "properties": {"name": ..., "timestamp": ...}}. Swift type differs from encoded type, and encoding needs to match a spec:</p>
</blockquote><p dir="auto">This comes very close to—but doesn't quite—address something else I'm concerned about. What's the preferred way to handle differences in serialization to different formats?<br>
<br>
Here's what I mean: Suppose I have a BlogPost model, and I can both fetch and post BlogPosts to a cross-platform web service, and store them locally. But when I fetch and post remotely, I ned to conform to the web service's formats; when I store an instance locally, I have a freer hand in designing my storage, and perhaps need to store some extra metadata. How do you imagine handling that sort of situation? Is the answer simply that I should use two different types?</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">This is a valid concern, and one that should likely be addressed.</p>
<p dir="auto">Perhaps the solution is to offer a <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">userInfo : [UserInfoKey : Any]</code> (<code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">UserInfoKey</code> being a <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">String</code>-<code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">RawRepresentable</code> struct or similar) on <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code> and <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Decoder</code> set at the top-level to allow passing this type of contextual information from the top level down.</p>
<p dir="auto"></p></div>
<div style="white-space:normal"><blockquote style="border-left:2px solid #777; color:#777; margin:0 0 5px; padding-left:5px"><blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto">To remedy both of these points, we adopt a new convention for inheritance-based coding — encoding super as a sub-object of self:</p>
</blockquote><p dir="auto">[snip]<br>
</p>
<blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto"> try super.encode(to: container.superEncoder())</p>
</blockquote><p dir="auto">This seems like a good idea to me. However, it brings up another point: What happens if you specify a superclass of the originally encoded class? In other words:<br>
<br>
        let joe = Employee(…)<br>
        let payload = try SomeEncoder().encode(joe)<br>
        …<br>
        let someone = try SomeDecoder().decode(Person.self, from: payload)<br>
        print(type(of: someone))                // Person, Employee, or does `decode(_:from:)` fail?</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">We don’t support this type of polymorphic decoding. Because no type information is written into the payload (there’s no safe way to do this that is not currently brittle), there’s no way to tell what’s in there prior to decoding it (and there wouldn’t be a reasonable way to trust what’s in the payload to begin with).<br>
We’ve thought through this a lot, but in the end we’re willing to make this tradeoff for security primarily, and simplicity secondarily.</p>
<p dir="auto"></p></div>
<div style="white-space:normal"><blockquote style="border-left:2px solid #777; color:#777; margin:0 0 5px; padding-left:5px"><blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto">The encoding container types offer overloads for working with and processing the API's primitive types (String, Int, Double, etc.). However, for ease of implementation (both in this API and others), it can be helpful for these types to conform to Codable themselves. Thus, along with these overloads, we will offer Codable conformance on these types:</p>
</blockquote><p dir="auto">[snip]<br>
</p>
<blockquote style="border-left:2px solid #777; color:#999; margin:0 0 5px; padding-left:5px; border-left-color:#999"><p dir="auto">Since Swift's function overload rules prefer more specific functions over generic functions, the specific overloads are chosen where possible (e.g. encode("Hello, world!", forKey: .greeting) will choose encode(_: String, forKey: Key) over encode<T : Codable>(_: T, forKey: Key)). This maintains performance over dispatching through the Codable existential, while allowing for the flexibility of fewer overloads where applicable.</p>
</blockquote><p dir="auto">How important is this performance? If the answer is "eh, not really that much", I could imagine a setup where every "primitive" type eventually represents itself as `String` or `Data`, and each `Encoder`/`Decoder` can use dynamic type checks in `encode(_:)`/`decode(_:)` to define whatever "primitives" it wants for its own format.</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">Does this imply that <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Int32</code> should decide how it’s represented as <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Data</code>? What if an encoder forgets to implement that?<br>
Again, we want to provide a static list of types that <code style="background-color:#F7F7F7; border-radius:3px; margin:0; padding:0 0.4em" bgcolor="#F7F7F7">Encoder</code>s know they <em>must</em> handle, and thus, consumers have <em>guarantees</em> that those types are supported.</p>
<p dir="auto"></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">* * *<br>
<br>
One more thing. In Alternatives Considered, you present two designs—#2 and #3—where you generate a separate instance which represents the type in a fairly standardized way for the encoder to examine.<br>
<br>
This design struck me as remarkably similar to the reflection system and its `Mirror` type, which is also a separate type describing an original instance. My question was: Did you look at the reflection system when you were building this design? Do you think there might be anything that can be usefully shared between them?</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">We did, quite a bit, and spent a lot of time considering reflection and its place in our design. Ultimately, the reflection system does not currently have the features we would need, and although the Swift team has expressed desire to improve the system considerably, it’s not currently a top priority, AFAIK.</p>
<p dir="auto"></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">Thank you for your attention. I hope this was helpful!</p>
</blockquote></div>
<div style="white-space:normal">
<p dir="auto">Thanks for all of these comments! Looking to respond to your other email soon.</p>
<p dir="auto"></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">-- <br>
Brent Royal-Gordon<br>
Architechies</p>
</blockquote></div>
<div style="white-space:normal">
</div>
</div>
</body>
</html>