<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Hi David,<div class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Apr 4, 2017, at 10:33 PM, David Hart 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=""><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span class=""></span></div><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class=""><span class=""></span></div><div class=""><div class=""><span class=""></span></div><div class=""><div class="">Very interesting discussion below. Here are a few more points:</div><div class=""><div class=""><br class="">Sent from my iPhone</div>On 4 Apr 2017, at 23:43, Itai Ferber via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div style="font-family: sans-serif;" class=""><div style="white-space: normal;" class=""><p dir="auto" class="">Hi Brent,</p><p dir="auto" class="">Thanks for your comments and thorough review! :)<br class="">Responses inline.</p><p dir="auto" class="">On 4 Apr 2017, at 1:57, Brent Royal-Gordon wrote:</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; color: rgb(153, 153, 153); margin: 0px 0px 5px; padding-left: 5px; border-left-color: rgb(153, 153, 153);" class=""><p dir="auto" class="">On Apr 3, 2017, at 1:31 PM, Itai Ferber via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:<br class="">Hi everyone,<br class=""><br class="">With feedback from swift-evolution and additional internal review, we've pushed updates to this proposal, and to the Swift Archival &amp; Serialization proposal.<br class="">Changes to here mostly mirror the ones made to Swift Archival &amp; Serialization, but you can see a specific diff of what's changed here. Full content below.<br class=""><br class="">We'll be looking to start the official review process very soon, so we're interested in any additional feedback.<br class=""><br class="">Thanks!<br class=""><br class="">— Itai</p></blockquote><p dir="auto" class="">This is a good revision to a good proposal.<br class=""><br class="">I'm glad `CodingKey`s now require `stringValue`s; I think the intended semantics are now a lot clearer, and key behavior will be much more reliable.</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">Agreed</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">I like the separation between keyed and unkeyed containers (and I think "unkeyed" is a good name, though not perfect), but I'm not quite happy with the unkeyed container API. Encoding a value into an unkeyed container appends it to the container's end; decoding a value from an unkeyed container removes it from the container's front. These are very important semantics that the method names in question do not imply at all.</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">I think that consistency of phrasing is really important here, and the action words "encode" and "decode" are even more important to connote than the semantics of pushing and popping.<br class="">(Note that there need not be specific directionality to an unkeyed container as long as the ordering of encoded items is eventually maintained on decode.) But on a practical note, names like<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">encodeAtEnd</code><span class="Apple-converted-space">&nbsp;</span>and<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">decodeFromFront</code><span class="Apple-converted-space">&nbsp;</span>(or similar) don't feel like they communicate anything much more useful than the current<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">encode</code>/<code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">decode</code>.</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">Certain aspects of `UnkeyedDecodingContainer` also feel like they do the same things as `Sequence` and `IteratorProtocol`, but in different and incompatible ways. And I certainly think that the `encode(contentsOf:)` methods on `UnkeyedEncodingContainer` could use equivalents on the `UnkeyedDecodingContainer`. Still, the design in this area is much improved compared to the previous iteration.</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">Which aspects of<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Sequence</code><span class="Apple-converted-space">&nbsp;</span>and<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">IteratorProtocol</code><span class="Apple-converted-space">&nbsp;</span>do you feel like you're missing on<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">UnkeyedDecodingContainer</code>? Keep in mind that methods on<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">UnkeyedDecodingContainer</code><span class="Apple-converted-space">&nbsp;</span>must be able to throw, and an<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">UnkeyedDecodingContainer</code>can hold heterogeneous items whose type is not known, two things that<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Sequence</code><span class="Apple-converted-space">&nbsp;</span>and<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">IteratorProtocol</code><span class="Apple-converted-space">&nbsp;</span>do not do.</p><p dir="auto" class="">In terms of an equivalent to<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">encode(contentsOf:)</code>, keep in mind that this would only work if the collection you're decoding is homogeneous, in which case, you would likely prefer to decode an<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Array</code><span class="Apple-converted-space">&nbsp;</span>over getting an unkeyed container, no? (As soon as conditional conformance arrives in Swift, we will be able to express<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">extension Array : Decodable where Element : Decodable { ... }</code><span class="Apple-converted-space">&nbsp;</span>making decoding homogeneous arrays trivial.)</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">(Tiny nitpick: I keep finding myself saying "encode into", not "encode to" as the API name suggests. Would that be a better parameter label?)</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">On a personal note here — I agree with you, and had originally used "into". However, we've reviewed our APIs and more often have balanced<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">from:/to:</code><span class="Apple-converted-space">&nbsp;</span>rather than<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">from:/into:</code><span class="Apple-converted-space">&nbsp;</span>on read/write/streaming calls. We'd like to rein these in a bit and keep them consistent within our naming guidelines, as much as possible.</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">I like the functionality of the `userInfo` dictionary, but I'm still not totally satisfied casting out of `Any` all the time. I might just have to get over that, though.</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">I think this is the closest we can get to a pragmatic balance between dynamic needs and static guarantees. :)</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">I wonder if `CodingKey` implementations might ever need access to the `userInfo`. I suppose you can just switch to a different set of `CodingKeys` if you do.</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">I don't think<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">CodingKey</code><span class="Apple-converted-space">&nbsp;</span>should ever know about<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">userInfo</code><span class="Apple-converted-space">&nbsp;</span>—<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">CodingKey</code>s should be inert data. If you need to, use the<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">userInfo</code><span class="Apple-converted-space">&nbsp;</span>to switch to a different set of keys, as you mention.</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">Should there be a way for an `init(from:)` implementation to determine the type of container in the encoder it's just been handed? Or perhaps the better question is, do we want to promise users that all decoders can tell the difference?</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">I think it would be very rare to need this type of information. If a type wants to encode as an array or as a dictionary conditionally, the context for that would likely be present in<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">userInfo</code>.<br class="">If you really must try to decode regardless, you can always try to grab one container type from the decoder, and if it fails, attempt to grab the other container type.</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">* * *<br class=""><br class="">I went ahead and implemented a basic version of `Encoder` and `Encodable` in a Swift 3 playground, just to get a feel for this system in action and experiment with a few things. A few observations:</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">Lots to unpack here, let's go one by one. :)</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">* I think it may make sense to class-constrain some of these protocols. `Encodable` and its containers seem to inherently have reference semantics—otherwise data could never be communicated from all those `encode` calls out to the ultimate caller of the API. Class-constraining would clearly communicate this to both the implementer and the compiler. `Decoder` and its containers don't *inherently* have reference semantics, but I'm not sure it's a good idea to potentially copy around a lot of state in a value type.</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">I don't think class constraints are necessary. You can take a look at the current implementation of<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">JSONEncoder</code><span class="Apple-converted-space">&nbsp;</span>and<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">JSONDecoder</code><span class="Apple-converted-space">&nbsp;</span><a href="https://github.com/itaiferber/swift/blob/3c59bfa749adad2575975e47130b28b731f763e0/stdlib/public/SDK/Foundation/JSONEncoder.swift" style="color: rgb(57, 131, 196);" class="">here</a><span class="Apple-converted-space">&nbsp;</span>(note that this is still a rough implementation and will be updated soon). The model I've followed there is that the encoder itself (<code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">_JSONEncoder</code>) has reference semantics, but the containers (<code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">_JSONKeyedEncodingContainer</code>,<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">_JSONUnkeyedEncodingContainer</code>) are value-type views into the encoder itself.</p><p dir="auto" class="">Keep in mind that during the encoding process, the entities created most often will be containers. Without some additional optimizations in place, you end up with a<span class="Apple-converted-space">&nbsp;</span><em class="">lot</em><span class="Apple-converted-space">&nbsp;</span>of small, short-lived class allocations as containers are brought into and out of scope.<br class="">By not requiring the class constraints, it's at least possible to make all those containers value types with references to the shared encoder.</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">* I really think that including overloads for every primitive type in all three container types is serious overkill. In my implementation, the primitive types' `Encodable` conformances simply request a `SingleValueEncodingContainer` and write themselves into it. I can't imagine any coder doing anything in their overloads that wouldn't be compatible with that, especially since they can never be sure when someone will end up using the `Encodable` conformance directly instead of the primitive. So what are all these overloads buying us? Are they just avoiding a generic dispatch and the creation of a new `Encoder` and perhaps a `SingleValueEncodingContainer`? I don't think that's worth the increased API surface, the larger overload sets, or the danger that an encoder might accidentally implement one of the duplicative primitive encoding calls inconsistently with the others.<br class=""><br class="">To be clear: In my previous comments, I suggested that we should radically reduce the number of primitive types. That is not what I'm saying here. I'm saying that we should always use a single value container to encode and decode primitives, and the other container types should always use `Encodable` or `Decodable`. This doesn't reduce the capabilities of the system at all; it just means you only have to write the code to handle a given primitive type one time instead of three.</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">Having implemented these myself multiple times, I agree — it can be a pain to repeat these implementations, and if you look at the linked implementations above, funneling to one method from all of those is exactly what I do (and in fact, this can be shortened significantly, which I plan on doing soon).</p><p dir="auto" class="">There is a tradeoff here between ease of use for the end consumer of the API, and ease of coding for the writer of a new<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Encoder</code>/<code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Decoder</code>, and my argument will always be for the benefit of the end consumer. (There will be orders of magnitude more end consumers of this API than those writing new<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Encoder</code>s and<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Decoder</code>s 😉)<br class="">Think of the experience for the consumer of this API, especially someone learning it for the first time. It can already be somewhat of a hurdle to figure out what kind of container you need, but even once you get a keyed container (which is what we want to encourage), then what? You start typing<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">container.enc...</code><span class="Apple-converted-space">&nbsp;</span>and in the autocomplete list in Xcode, the only thing that shows up is one autocomplete result:<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">encode(value: Encodable, forKey: ...)</code><span class="Apple-converted-space">&nbsp;</span>Consider the clarity (or lack thereof) of that, as opposed to seeing<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">encode(value: Int, forKey: ...)</code>,<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">encode(value: String, forKey: ...)</code>, etc. Given a list of types that users are already familiar with helps immensely with pushing them in the right direction and reducing cognitive load. When you see<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">String</code><span class="Apple-converted-space">&nbsp;</span>in that list, you don't have to question whether it's possible to encode a string or not, you just pick it. I have an<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Int8</code>, can I encode it? Ah, it's in the list, so I can.</p><p dir="auto" class="">Even for advanced users of the API, though, there's something to be said for static guarantees in the overloading. As someone familiar with the API (who might even know all the primitives by heart), I might wonder if the<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Encoder</code><span class="Apple-converted-space">&nbsp;</span>I'm using has correctly switched on the generic type. (It would have to be a dynamic switch.) Did the implementer remember to switch on<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Int16</code><span class="Apple-converted-space">&nbsp;</span>correctly? Or did they forget it and will I be falling into a generic case which is not appropriate here?</p><p dir="auto" class="">When it comes to overloads vs. dynamically switching on a generic type, I think we would generally prefer the static type safety. As a consumer of the API I want to be sure that the implementer of the<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Encoder</code><span class="Apple-converted-space">&nbsp;</span>I'm using was aware of these primitive types in some way, and that the compiler helped them too to make sure they didn't, say, forget to switch on<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Data.self</code>. As a writer of<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Encoder</code>s, yes, this is a pain, but a sacrifice I'm willing to make for the sake of the consumer.</p><p dir="auto" class="">Let's take a step back, though. This is mostly annoying to implement because of the repetition, right? If someone were to put together a proposal for a proper macro system in Swift, which is<span class="Apple-converted-space">&nbsp;</span><em class="">really</em><span class="Apple-converted-space">&nbsp;</span>what we want here, I wouldn't be sad. 😉</p></div></div></div></blockquote><div class="">There's also an argument of API surface area. As a user or implementer of the API, it's much less intimidating to load the documentation for a protocol and see one central function than many overloads.</div><div class=""><br class=""></div><div class="">I've used many serialization third-party frameworks in Swift. None of them defined all those overloads, and more importantly, I never saw any user of those APIs post an issue to GitHub where the cause could be traced back to the lack of those overloads.</div><div class=""><br class=""></div><div class="">These overloads look to me like remnants of Codable's NSCoding influences instead of an API reimagined for Swift.</div><div class=""><br class=""></div><div class="">For the same reasons, I continue to believe that decode functions should overload on the return type. If we follow the arguments in favor of providing a type argument, then why don't we also have type arguments for encoders: encode(_ value: T?, forKey key: Key, as type: T.self)? I'm not advocating that: I'm just pushing the argument to its logical conclusion to explain why I don't understand it.</div></div></div></div></div></blockquote><div><br class=""></div>I don’t see a way for a call to encode to become ambiguous by omitting the type argument, whereas the same is not true for a return value from decode. The two seem fundamentally different.</div><div><br class=""></div><div>- Tony</div><div><br class=""><blockquote type="cite" class=""><div class=""><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class=""><div class=""><blockquote type="cite" class=""><div class=""><div style="font-family: sans-serif;" class=""><div style="white-space: normal;" class=""><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">* And then there's the big idea: Changing the type of the parameter to `encode(to:)` and `init(from:)`.<br class=""><br class="">***<br class=""><br class="">While working with the prototype, I realized that the vast majority of conformances will immediately make a container and then never use the `encoder` or `decoder` again. I also noticed that it's illegal to create more than one container from the same coder, and there are unenforceable preconditions to that effect. So I'm wondering if it would make sense to not pass the coder at all, but instead have the conforming type declare what kind of container it wants:<br class=""><br class="">extension Pet: Codable {<br class="">init(from container: KeyedDecodingContainer&lt;CodingKeys&gt;) throws {<br class="">name = try container.decode(String.self, forKey: .name)<br class="">age = try container.decode(Int.self, forKey: .age)<br class="">}<br class=""><br class="">func encode(to container: KeyedEncodingContainer&lt;CodingKeys&gt;) throws {<br class="">try container.encode(name, forKey: .name)<br class="">try container.encode(age, forKey: .age)<br class="">}<br class="">}<br class=""><br class="">extension Array: Encodable where Element: Encodable {<br class="">init(from container: UnkeyedDecodingContainer) throws {<br class="">self.init()<br class="">while !container.isAtEnd {<br class="">append(try container.decode(Element.self))<br class="">}<br class="">}<br class=""><br class="">func encode(to container: UnkeyedEncodingContainer) throws {<br class="">container.encode(contentsOf: self)<br class="">}<br class="">}<br class=""><br class="">I think this could be implemented by doing the following:<br class=""><br class="">1. Adding an associated type to `Encodable` and `Decodable` for the type passed to `encode(to:)`/`init(from:)`.</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">This is already unfortunately a no-go. As mentioned in other emails, you cannot override an<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">associatetype</code><span class="Apple-converted-space">&nbsp;</span>in a subclass of a class, which means that you cannot require a different container type than your superclass. This is especially problematic in the default case where we'd want to encourage types to use keyed containers — every type should have its own keys, and you'd need to have a different keyed container than your parent, keyed on your keys.</p><p dir="auto" class="">Along with that, since the<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">typealias</code><span class="Apple-converted-space">&nbsp;</span>would have to be at least as visible as your type (potentially<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">public</code>), it would necessitate that your key type would be at least as public as your type as well. This would expose your type's coding keys, which is prohibitive. (Consider what this would mean for frameworks, for instance.)</p><p dir="auto" class="">Finally, this also means that you could not request different container types based on context — a type could not offer both a dictionary representation and a more efficient array representation, since it can only statically request one container type.</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">2. Creating protocols for the types that are permitted there. Call them `EncodingSink` and `DecodingSource` for now.<br class=""><br class="">3. Creating *simple* type-erased wrappers for the `Unkeyed*Container` and `SingleValue*Container` protocols and conforming them to `EncodingSink` and `DecodingSource`. These wouldn't need the full generic-subclass dance usually used for type-erased wrappers; they just exist so you can strap initializers to them. In a future version of Swift which allowed initializers on existentials, we could probably get rid of them.<br class=""><br class="">(Incidentally, if our APIs always return a type-erased wrapper around the `Keyed*ContainerProtocol` types, there's no actual need for the underlying protocols to have a `Key` associated type; they can use `CodingKey` existentials and depend on the wrapper to enforce the strong key typing. That would allow us to use a simple type-erased wrapper for `Keyed*Container`, too.)<br class=""><br class="">4. For advanced use cases where you really *do* need to access the encoder in order to decide which container type to use, we would also need to create a simple type-erased wrapper around `Encoder` and `Decoder` themselves, conforming them to the `Sink`/`Source` protocols.</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">This might address my last point above, but then what useful interface would<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">EncodingSink</code><span class="Apple-converted-space">&nbsp;</span>and<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">DecodingSource</code><span class="Apple-converted-space">&nbsp;</span>have if a type conforming to<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">EncodingSink</code><span class="Apple-converted-space">&nbsp;</span>could be any one of the containers or even a whole encoder itself?</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">5. The Source/Sink parameter would need to be `inout`, unless we *do* end up class-constraining things. (My prototype didn't.)<br class=""><br class="">There are lots of little details that change too, but these are the broad strokes.<br class=""><br class="">Although this technically introduces more types, I think it actually simplifies the design for people who are just using the `Codable` protocol. All they have to know about is the `Codable` protocol, the magic `CodingKeys` type, the three container types (realistically, probably just the `KeyedEncoding/DecodingContainer`), and the top-level encoders they want to use. Most users should never need to know about the members of the `Encoder` protocol; few even need to know about the other two container types. They don't need to do the "create a container" dance. The thing would just work with a minimum of fuss.<br class=""><br class="">Meanwhile, folks who write encoders *do* deal with a bit more complexity, but only because they have to be aware of more type-erased wrappers. In other respects, it's simpler for them, too. Keyed containers don't need to be generic, and they have a layer of Foundation-provided wrappers above them that can help enforce good behavior and (probably) hide the implementation a little bit more. I think that overall, it's probably better for them, too.<br class=""><br class="">Thoughts?</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">For what it's worth, the way to introduce these three different types of encoding<span class="Apple-converted-space">&nbsp;</span><em class="">without</em><span class="Apple-converted-space">&nbsp;</span>the use of associated types is to split the<span class="Apple-converted-space">&nbsp;</span><code bgcolor="#F7F7F7" style="background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px; padding: 0px 0.4em;" class="">Codable</code><span class="Apple-converted-space">&nbsp;</span>protocol up into three protocols, which<span class="Apple-converted-space">&nbsp;</span><a href="https://github.com/itaiferber/swift-evolution/blob/swift-archival-serialization/proposals/XXXX-swift-archival-serialization.md#alternatives-considered" style="color: rgb(57, 131, 196);" class="">we've tried in the past</a><span class="Apple-converted-space">&nbsp;</span>(bullet #4). Unfortunately, the results are not great — an even bigger explosion of types, overloads, etc.</p><p dir="auto" class="">While I agree that the current approach of dynamically requesting containers is, well, dynamic, the benefit of not exposing your keys publicly and allowing encoding of classes is a big win in comparison, I think.</p><p dir="auto" class="">I am curious, though, about your comment above on preconditions being unenforceable, because this is certainly something we would want to hammer out before releasing. What cases are you thinking of that are unenforceable?</p><div class=""><br class="webkit-block-placeholder"></div></div><div style="white-space: normal;" class=""><blockquote style="border-left-width: 2px; border-left-style: solid; border-left-color: rgb(119, 119, 119); color: rgb(119, 119, 119); margin: 0px 0px 5px; padding-left: 5px;" class=""><p dir="auto" class="">--<span class="Apple-converted-space">&nbsp;</span><br class="">Brent Royal-Gordon<br class="">Architechies</p></blockquote></div><div style="white-space: normal;" class=""><p dir="auto" class="">Again, thanks for your thorough review! Looking forward to further comments. :)</p></div></div></div></blockquote><blockquote type="cite" class=""><div class=""><span class="">_______________________________________________</span><br class=""><span class="">swift-evolution mailing list</span><br class=""><span class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a></span><br class=""><span class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a></span><br class=""></div></blockquote></div></div></div><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">_______________________________________________</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">swift-evolution mailing list</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><a href="mailto:swift-evolution@swift.org" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">swift-evolution@swift.org</a><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a></div></blockquote></div><br class=""></div></body></html>