<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 Mar 17, 2017, at 5:13 PM, Brent Royal-Gordon <<a href="mailto:brent@architechies.com" class="">brent@architechies.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class="">On Mar 17, 2017, at 2:38 PM, Matthew Johnson <<a href="mailto:matthew@anandabits.com" class="">matthew@anandabits.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="" 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;"><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class="">At a broad level, that's a good idea. But why not provide something more precise than a bag of `Any`s here? You're in pure Swift; you have that flexibility.</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>protocol Codable {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>associatedtype CodingContext = ()</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>init<Coder: Decoder>(from decoder: Coder, with context: CodingContext) throws</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>func encoder<Coder: Encoder>(from encoder: Coder, with context: CodingContext) throws</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>protocol Encoder {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>associatedtype CodingContext = ()</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>func container<Key : CodingKey>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key, CodingContext></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>…</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>class KeyedEncodingContainer<Key: CodingKey, CodingContext> {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>func encode<Value: Codable>(_ value: Value,? forKey key: Key, with context: Value.CodingContext) throws { … }</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>// Shorthand when contexts are the same:</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>func encode<Value: Codable>(_ value: Value,? forKey key: Key) throws</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                        </span>where Value.CodingContext == CodingContext</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>{ … }</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>…</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div></div></div></blockquote><div 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-stroke-width: 0px;" class=""><br class=""></div><div 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-stroke-width: 0px;" class="">This is sort of similar to the design I suggested for contexts. The difference is that you’re requiring all Codable to be context aware and by introducing an associated type you break the ability to use Codable as an existential.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">I don't think banning existentials is actually a loss. Since `encode(_:)` doesn't record type information, and instead `decode(_:)` requires the exact concrete type to be passed in, `Codable` existentials cannot be usefully encoded or decoded. For instance, a heterogeneous `[Codable]` would encode in several different, probably mutually incompatible formats, without any type information that could distinguish between them. Since the only semantics of `Codable` are encoding and decoding, and decoding is always done by an `init`, `Codable` existentials are useless and we lose nothing by not supporting them.</div></div></div></div></blockquote><div><br class=""></div><div>That’s fair. But how would you change the design of the NSKeyedArchiver / NSKeyedUnarchiver extensions which use the existentials? </div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><br 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 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-stroke-width: 0px;" class="">Many Codable conforming types won’t need to know anything about a context. I would still want to be able to encode them along with my custom context-aware types. A good example is types from Foundation that will conform to Codable. They will definitely not know anything about my context but I still want to be able to encode a URL alongside my custom context-aware types.</div><div 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-stroke-width: 0px;" class=""></div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Sure; you can do that by calling `encode(_:forKey:with:)` and passing a freshly-made `()` context. We might even add a second convenience overload of `encode(_:forKey:)`:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>class KeyedEncodingContainer<Key: CodingKey, CodingContext> {<br class=""><span class="Apple-tab-span" style="white-space:pre">                </span>func encode<Value: Codable>(_ value: Value,? forKey key: Key, with context: Value.CodingContext) throws { … }<br class=""><span class="Apple-tab-span" style="white-space:pre">                </span><br class=""><span class="Apple-tab-span" style="white-space:pre">                </span>// Shorthand when contexts are the same:<br class=""><span class="Apple-tab-span" style="white-space:pre">                </span>func encode<Value: Codable>(_ value: Value,? forKey key: Key) throws<br class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>where Value.CodingContext == CodingContext<br class=""><span class="Apple-tab-span" style="white-space:pre">                </span>{</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>try encode(value, forKey: key, with: currentContext)</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>// Shorthand when the type uses a Void context:</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>func encode<Value: Codable>(_ value: Value,? forKey key: Key) throws<br class=""><span class="Apple-tab-span" style="white-space: pre;">                        </span>where Value.CodingContext == Void<br class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>{</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>try encode(value, forKey: key, with: ())</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span><br class=""><span class="Apple-tab-span" style="white-space:pre">                </span>…<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><br class=""></div><div class="">The main disadvantage I can think of in this design is that even `Codable` users who don't need a context have to have a `with context: Void` in their code. This might be confusing to new developers, but I think it's worth it.</div><div class=""><br class=""></div><div class="">(I don't think I mentioned this anywhere, but containers like `Array` should take on the `CodingContext` of their `Element`s and pass the context they receive through without examining it. That would probably be pretty common with generic container types.)</div></div></div></div></blockquote><div><br class=""></div><div>You’re right - I just wasn’t thinking about this clearly. I missed that you were requiring Codable types to manually thread the context through. This is kind of unfortunate when *all* types involved in the encoding either have a Void context or use the same context type. On the other hand, it is a somewhat rarely needed feature and this approach offers a lot of flexibility. I think I like it.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><br 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 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-stroke-width: 0px;" class="">Did you take a look at the design I suggested? What do you think of it?</div></div></div></div></blockquote><div class=""><br class=""></div>I think that, if a type wants to support context-free coding, it should use an optional `CodingContext`. :^)</div><div class=""><br class=""></div><div class="">In all seriousness, I see the design as very slightly weak, in that it makes it easy to forget to pass a context through, but quite acceptable. </div></div></div></blockquote><div><br class=""></div><div>Easy for who? I was not requiring Codable types to thread it through at all. The context was fully managed by the Encoder / Decoder type. The only place Codable types work with the context is as an argument they receive. They never pass it when encoding or decoding anything. The Encoder / Decoder would need to store the context internally and when call is made to encode / decode a ContextAwareCodable it would pass the result of a dynamic cast to ContextAwareCodable.Context as the context.</div><div><br class=""></div><div>This design encapsulates the context more completely and solves all the real world use cases I know of at the expense of some flexibility. It also guarantees that *all* Codables in a single encoding / decoding see exactly the same context or no context at all. This could be viewed as an advantage or a disadvantage.</div><div><br class=""></div><div>Maybe your approach of making the context more exposed but also offering more flexibility and guaranteeing a Codable always gets the context it needs is better. I need more time to think about it, but I think it makes better tradeoffs.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">It would certainly solve the `with context: Void` problem I mentioned. I might consider reversing the relationship between the two protocols, though:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>public protocol ContextAwareCodable {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>associatedtype CodingContext</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>init(from decoder: Decoder, with context: CodingContext) throws<br class=""><span class="Apple-tab-span" style="white-space:pre">                </span>func encode(to encoder: Encoder, with context: CodingContext) throws</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>public protocol Codable: ContextAwareCodable where CodingContext == Void {</div><div class=""><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>init(from decoder: Decoder) throws<br class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>func encode(to encoder: Encoder) throws</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>extension Codable {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>public init(from decoder: Decoder, with context: Void) throws {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>try self.init(from: decoder)</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>func encode(to encoder: Encoder, with context: Void) throws {</div></div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>try encode(to: encoder)</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class="">Most `Encoder`/`Decoder` APIs would have to use `ContextAwareCodable`, but if you're writing a coder, you'd better be aware of contexts.</div></div></div></blockquote><div><br class=""></div><div>The reason I did it the other way is to allow Codable to be used as an existential. It is used that way in the NSKeyedArchiver / NSKeyedUnarchiver extensions and I wanted to find something workable that wouldn’t break that. If we aren’t worried about existentials then this would work.</div><div><br class=""></div><div>Agree - the design priority should be for users and authors of Codable types. Encoders and Decoders are comparatively rare and written by people who should know what they are doing.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><br class=""></div><div class="">* * *</div><div class=""><br class=""></div><div class="">A thought I just had: Someone upthread mentioned that `Codable` might be better as part of the standard library. One reason to favor that approach is that you could then make `Codable` support a requirement of types like `BinaryInteger` and `FloatingPoint`.</div><div class=""><br class=""></div><div class="">It might still make sense to have the coders themselves be part of Foundation; only the protocols defining `Codable`, `Encoder`, `Decoder`, and their ancillary types would be part of the standard library.</div></div></div></blockquote><div><br class=""></div><div>+1 to putting the protocols in the standard library and keeping the concrete encoders and decoders in Foundation.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div class="">
<span class="Apple-style-span" style="border-collapse: separate; font-variant-ligatures: normal; font-variant-position: normal; font-variant-numeric: normal; font-variant-alternates: normal; font-variant-east-asian: 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=""></div></div></blockquote></div><br class=""></body></html>