<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Apr 12, 2017, at 6:18 PM, Russ Bishop <<a href="mailto:rbishopjr@apple.com" class="">rbishopjr@apple.com</a>> 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=""><blockquote type="cite" class=""><div class=""><br class="Apple-interchange-newline">On Apr 12, 2017, at 5:44 PM, Brent Royal-Gordon <<a href="mailto:brent@architechies.com" class="">brent@architechies.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class="">On Apr 12, 2017, at 11:44 AM, Russ Bishop via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><div class=""><div class="" 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;"><div class=""><br class=""></div><div class="">* String and Int keys being optional, giving a CodingKey the opportunity to return nil for both at runtime.</div></div></div></blockquote><div class=""><br class=""></div><div class="">This was true in the first public draft, but I believe in this version `stringValue` is no longer Optional.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">You are correct; I still don't like the fact that you can be in an unkeyed archiver situation and hitting CodingKey adopters that don't provide integer keys. </div></div></div></blockquote><div><br class=""></div><div>You are building this dislike on top of several misconceptions:</div><div><br class=""></div><div>* There are no "keyed archivers" and "unkeyed archivers". There are keyed and unkeyed *containers*, and all coders handle both kinds.</div><div><br class=""></div><div>* Unkeyed containers don't use keys at all, so this has nothing to do with keyed vs. unkeyed. Unkeyed containers are for storing a series of values with no inherent structure to them, only an order. Array elements and tuples are good examples. (But of course, you'd just encode the array as a whole into an entry in a keyed container and the array itself would create an unkeyed container and put its elements into it.)</div><div><br class=""></div><div>* Integer keys are a space-efficient alternative to identifying keys by string value; they give supporting coders a fixed-width value they can use to represent a key. But they don't need to be contiguous and they don't necessarily have any influence on the order that values are encoded in. As long as you don't change an existing field's integerValue or reuse an integerValue previously assigned to a different key, there should be no versioning problems.</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=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" 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;"><div class="">* Encoder has three functions, but only one may ever be called. This API is the opposite of "pit of success".</div><div class=""></div></div></div></blockquote><div class=""><br class=""></div><div class="">Personally, I'm worried about that too. But I had a lot of trouble coming up with an alternative that didn't violate a more important goal, like "being able to throw errors" or "being compatible with classes".</div><div class=""><br class=""></div><div class="">Last-ditch suggestion: Change a bunch of names so that, for instance, `Encoder` is instead `EncodingContainerizer`. (That's a terrible name, but "Factory" gives me the hives.) That way, the name of the type gives you a hint that you're supposed to use it to make a container. You might even rename the methods to e.g. `useContainer(keyedBy:)`, which sound a little more stateful and mutually-exclusive.</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>func encode(to encoder: EncodingContainerizer) throws {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>var container = encoder.useContainer(keyedBy: CodingKeys.self)</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>try container.encode(name, forKey: .name)</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>try container.encode(birthDate, forKey: .birthDate)</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">We could just completely separate the idea of keyed and unkeyed encoding?</div><div class=""></div></div></div></blockquote><div><br class=""></div><div>As I said, I think you misunderstand what these things are for.</div><div><br class=""></div><div>I would like, though, to have the type declare the kind of container it wants. In a previous thread, I suggested that `encode(to:)` should take a container directly, and we should construct it for them:</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>func encode(to container: KeyedEncodingContainer<CodingKeys>) throws {</div><div><div class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>try container.encode(name, forKey: .name)</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>try container.encode(birthDate, forKey: .birthDate)</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class="">The authors pointed out that this would require you to make the `CodingKeys` type public and would mean that subclasses couldn't switch container types. These really are important problems with any idea of that sort.</div><div class=""><br class=""></div></div></div></div></div></div><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=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class="">I argued about this pretty vigorously. They want to avoid the overhead of building an encoder and single-value container and then making several generic calls to encode the value into the container. Personally, I think this is premature optimization of the highest order—particularly since building an encoder and single-value container are often no-ops—but this is the design they chose, and I don't think it's worth rejecting for that alone.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Has someone done performance tests to validate that this is an issue? If nothing else this seems like an implementation detail a conforming type could opt to provide but it shouldn't be required in every implementation.</div></div></div></blockquote><div><br class=""></div><div>I don't know what testing they've done, but this is not something that individual coders could add after the fact. `encode(to:)` and `init(from:)` are passed only protocol existentials containing the coder, so they don't have access to any member that isn't in the protocol. The same for containers. Accessing any special, coder-specific methods would perhaps not be impossible, but it'd be prohibitively difficult. So if any coders want to have this fast path, it needs to be part of the protocol ahead of time; it can't be bolted on by only the conforming types that want it.</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=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><div class="">I think they've done a reasonable job of putting unkeyed coding in a sharps drawer by making you specifically ask for it and giving it an ugly name. </div></div></div></blockquote><div class=""><br class=""></div><div class="">Can you clarify what you mean? I see keyed and unkeyed spread across various protocols and types in the proposal. It's front and center. </div></div></div></blockquote><div><br class=""></div><div>What I mean is that, when you get an encoder, you have three choices: `container(keyedBy:)`, `unkeyedContainer()`, and `singleValueContainer()`. If you looked into single-value, you'd quickly discover that it only supports a few low-level types (actually, I think it might be better to call it `singlePrimitiveValueContainer()`. So that leaves you with keyed and unkeyed. `unkeyedContainer()` has an ugly, awkward name (this is actually intentional), and just about every piece of documentation or tutorial is going to tell you not to use it. Meanwhile, `container(keyedBy:)` is nice and we generate all sorts of code and stuff to make it easy to use. Once you're into a keyed container, there's no way to accidentally use any unkeyed methods.</div><div><br class=""></div><div>And all of this is only an issue if you write the conformance by hand in the first place. Usually, you won't.</div></div><br class=""><div class="">
<span class="Apple-style-span" style="border-collapse: separate; font-variant-ligatures: normal; font-variant-east-asian: normal; font-variant-position: normal; line-height: normal; border-spacing: 0px;"><div class=""><div style="font-size: 12px; " class="">-- </div><div style="font-size: 12px; " class="">Brent Royal-Gordon</div><div style="font-size: 12px; " class="">Architechies</div></div></span>
</div>
<br class=""></body></html>