<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=""><div><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><br class=""></div><div>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><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><br class=""></div><div>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><br class=""></div><div><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><span class="Apple-tab-span" style="white-space:pre">                        </span>try encode(value, forKey: key, with: currentContext)</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div><span class="Apple-tab-span" style="white-space:pre">                </span></div><div><span class="Apple-tab-span" style="white-space:pre">                </span>// Shorthand when the type uses a Void context:</div><div><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><span class="Apple-tab-span" style="white-space:pre">                        </span>try encode(value, forKey: key, with: ())</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div><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>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><br class=""></div><div>(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><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><br class=""></div>I think that, if a type wants to support context-free coding, it should use an optional `CodingContext`. :^)</div><div><br class=""></div><div>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. It would certainly solve the `with context: Void` problem I mentioned. I might consider reversing the relationship between the two protocols, though:</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>public protocol ContextAwareCodable {</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>associatedtype CodingContext</div><div><span class="Apple-tab-span" style="white-space:pre">                </span></div><div><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><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>public protocol Codable: ContextAwareCodable where CodingContext == Void {</div><div><div><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><span class="Apple-tab-span" style="white-space:pre">                        </span>try encode(to: encoder)</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div><br class=""></div><div>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><br class=""></div><div>* * *</div><div><br class=""></div><div>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><br class=""></div><div>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><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=""></body></html>