<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 19, 2017, at 5:51 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=""><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="">I generally agree with you about casting. However, my dislike isn’t the cast itself, but instead it is the lack of a static guarantee. I’m not sure we’ll find a solution that provides a static guarantee that a required context exists that is also acceptable to the Foundation team.</div></div></div></div></blockquote><div><br class=""></div><div>I don't think we can get a static guarantee that the context is present, but I still would like a static guarantee that the context is of the expected type. That's what I'm trying to provide here.</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=""><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=""><br class=""></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>// Retrieve the context instance of the indicated type.</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>func context<Context>(ofType type: Context.Type) -> Context?</div></div></div></blockquote><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=""><span class="Apple-tab-span" style="white-space: pre;">                </span></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>// This context is visible for `encode(_:)` calls from this encoder's containers all the way down, recursively.</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>func addContext<Context>(_ context: Context, ofType type: Context.Type)</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="">What happens if you call `addContext` more than once with values of the same type?</div></div></div></div></blockquote><div><br class=""></div><div>It overrides the previous context, but only for the containers created by this `encode(to:)` method and any containers nested within them.</div><div><br class=""></div><div>(Although that could cause trouble for an encoder which only encodes objects with multiple instances once. Hmm.)</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="">And why do you require the type to be passed explicitly when it is already implied by the type of the value?</div></div></div></div></blockquote><div><br class=""></div><div>As you surmised later, I was thinking in terms of `type` being used as a dictionary key; in that case, if you stored a `Foo` into the context, you would not later be able to look it up using one of `Foo`'s supertypes. But if we really do expect multiple contexts to be rare, perhaps we don't need a dictionary at all—we can just keep an array, loop over it with `as?`, and return the first (or last?) match. If that's what we do, then we probably don't need to pass the type explicitly.</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=""><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=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>// Likewise on 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>// Encoder and decoder classes should accept contexts in their top-level API:</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>open class JSONEncoder {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>open func encode<Value : Codable>(_ value: Value, withContexts contexts: [Any] = []) throws -> Data</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="">What happens if more than one context of the same type is provided here?</div></div></div></div></blockquote><div><br class=""></div><div>Fail a precondition, probably.</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="">Also, it’s worth pointing out that whatever reason you had for explicitly passing the type above you’re not requiring type information to be provided here. Whatever design we have it should be self-consistent.</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>Yeah. I did this here because there was no way to specify a dictionary literal of `(T.Type, T)`, where `T` could be different for different elements.</div><br 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 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="">Do you think it’s really important to allow users to dynamically provide context for children? Do you have real world use cases where this is needed? I’m sure there could be case where this might be useful. But I also think there is some benefit in knowing that the context used for an entire encoding / decoding is the one you provide at the top level. I suspect the benefit of a static guarantee that your context is used for the entire encoding / decoding has a lot more value than the ability to dynamically change the context for a subtree.</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></blockquote><div><br class=""></div><div>The problem with providing all the contexts at the top level is that then the top level has to *know* what all the contexts needed are. Again, if you're encoding a type from FooKit, and it uses a type from GeoKit, then you—the user of FooKit—need to know that FooKit uses GeoKit and how to make contexts for both of them. There's no way to encapsulate GeoKit's role in encoding.</div><div><br class=""></div><div>On the other hand, there *could* be a way to encapsulate it. Suppose we had a context protocol:</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>protocol CodingContext {</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>var underlyingContexts: [CodingContext] { get }</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>extension CodingContext {</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>var underlyingContexts: [CodingContext] { return [] }</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div><br class=""></div><div>Then you could have this as your API surface:</div><div><br class=""></div><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>// Retrieve the context instance of the indicated type.</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>func context<Context: CodingContext>(ofType type: Context.Type) -> Context?</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>// Likewise on 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>// Encoder and decoder classes should accept contexts in their top-level API:</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>open class JSONEncoder {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>open func encode<Value : Codable>(_ value: Value, with context: CodingContext? = nil) throws -> Data</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""><br class=""></div><div class="">And libraries would be able to add additional contexts for dependencies as needed.</div><div class=""><br class=""></div><div class="">(Hmm. Could we maybe do this?</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 CodingContextType: CodingContext = Never</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) throws</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>init(from decoder: Decoder) throws</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class=""><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>// Retrieve the context instance of the indicated type.</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>func context<CodableType: Codable>(for instance: Codable) -> CodableType.CodingContextType?</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>// Likewise on 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>// Encoder and decoder classes should accept contexts in their top-level API:</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>open class JSONEncoder {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>open func encode<Value : Codable>(_ value: Value, with context: Value.CodingContextType? = nil) throws -> Data</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div></div><div class=""><br class=""></div><div class="">That would make sure that, if you did use a context, it would be the right one for the root type. And I don't believe it would have any impact on types which didn't use contexts.)</div></div><br 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 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="">What benefit do you see in using types as context “keys” rather than something like `CodingUserInfoKey`? As far as I can tell, it avoids the need for an explicit key which you could argue are somewhat redundant (it would be weird to have two context values of the same type in the cases I know of) and puts the cast in the Encoder / Decoder rather than user code. These seem like modest, but reasonable wins. </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></blockquote><div><br class=""></div><div>I also see it as an incentive for users to build a single context type rather than sprinkling in a whole bunch of separate keys. I really would prefer not to see people filling a `userInfo` dictionary with random primitive-typed values like `["json": true, "apiVersion": "1.4"]`; it seems too easy for names to clash or people to forget the type they're actually using. `context(…)` being a function instead of a subscript is similarly about ergonomics: it discourages you from trying to mutate your context during the encoding process (although it doesn't prevent it for reference types.)</div><br 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 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="">Unfortunately, I don't think there is a good answer to the question about multiple context values with the same type though. I can’t think of a good way to prevent this statically. Worse, the values might not have the same type, but be equally good matches for a type a user requests (i.e. both conform to the same protocol). I’m not sure how a user-defined encoder / decoder could be expected to find the “best” match using semantics that would make sense to Swift users (i.e. following the rules that are kind of the inverse to overload resolution). </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=""><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="">Even if this were possible there are ambiguous cases where there would be equally good matches. Which value would a user get when requesting a context in that case? We definitely don’t want accessing the context to be a trapping or throwing operation. That leaves returning nil or picking a value at random. Both are bad choices IMO.</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></blockquote><div><br class=""></div><div>If we use the `underlyingContexts` idea, we could say that the context list is populated breadth-first and the first context of a particular type encountered wins. That would tend to prefer the context "closest" to the top-level one provided by the caller, which will probably have the best fidelity to the caller's preferences.</div></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>