<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div><br><br>Sent from my iPad</div><div><br>On Mar 20, 2017, at 1:43 AM, David Hart <<a href="mailto:david@hartbit.com">david@hartbit.com</a>> wrote:<br><br></div><blockquote type="cite"><div><meta http-equiv="content-type" content="text/html; charset=utf-8"><div><br></div><div>On 20 Mar 2017, at 02:09, Matthew Johnson via swift-evolution <<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>> wrote:<br><br></div><blockquote type="cite"><div><meta http-equiv="Content-Type" content="text/html charset=utf-8"><br class=""><div><blockquote type="cite" class=""><div class="">On Mar 19, 2017, at 2:21 PM, Tony Parker <<a href="mailto:anthony.parker@apple.com" class="">anthony.parker@apple.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=""><br class=""><div class=""><blockquote type="cite" class=""><div class="">On Mar 19, 2017, at 12:14 PM, Matthew Johnson <<a href="mailto:matthew@anandabits.com" class="">matthew@anandabits.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="content-type" content="text/html; charset=utf-8" class=""><div dir="auto" class=""><div class=""><br class=""><br class="">Sent from my iPhone</div><div class=""><br class="">On Mar 19, 2017, at 10:47 AM, Tony Parker <<a href="mailto:anthony.parker@apple.com" class="">anthony.parker@apple.com</a>> wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class="">Hi Matthew, Brent,<div class=""><br class=""></div><div class="">I see why you are asking for this Context parameter, but putting it into the basic Codable protocol introduces too much conceptual overhead. There are too many benefits to keeping adoption to just one protocol, including discoverability, ease of use, reducing the need for overloads on protocols elsewhere, and more. Supporting this one use case does not outweigh those benefits, especially considering I expect that most library code would not use it (as you say: it would be weird to pass this context between modules).</div><div class=""><br class=""></div><div class="">Can you figure out a way to get the context info passed through the encoder/decoder instead? It would make more sense as something optionally retrieved from the encoder/decoder that was set at the top level.</div></div></blockquote><div class=""><br class=""></div><div class="">Hi Tony. <span style="background-color: rgba(255, 255, 255, 0);" class="">I can see the argument that the this is a feature that should be relatively rarely used and thus should have as simple a design as possible.</span></div><div class=""><br class=""></div><div class="">If you feel like the impact of threading a typed context on the API surface area is too heavy you could just add a `var context: Any? { get }` requirement to Encoder and Decoder. The expectation is that encoders and decoders would accept a context in the top level call and make it available to all Codable types. This would solve the problem with minimal API impact at the cost of the ability to statically verify that all types receive the context they need to encode / decode correctly.</div><div class=""><br class=""></div><div class="">I much prefer the static safety but having a solution is better than not having one. :)</div></div></div></blockquote><div class=""><br class=""></div>The Any context property is reasonable, but it would be nice to find something in the middle. =)</div><div class=""><br class=""></div><div class="">One other possibility is that we define a user info dictionary instead, with a custom key type that can be extended (much like our string enumerations). In general I haven’t been a fan of the user info pattern in Swift because of the necessity to cast, but as you say it’s better than nothing. e.g. userInfo : [CodingUserInfoKey: Any].</div></div></div></blockquote><div><br class=""></div><div>This makes sense some sense. This would allow us to support the multiple context use case. I think the need for that is far more rare than the need for a single context but it still makes sense to support it.</div></div></div></blockquote><div><br></div><div>I'm not a fan of the info dictionary as it penalizes the simple cases of a unique context type.</div></div></blockquote><div><br></div><div>I agree with this. That is why I suggested we need to add convenience for the single context of a custom type if we go that route. I like the context stack idea I posted later last night better though. It gets away from the context dictionary altogether.</div><br><blockquote type="cite"><div><br><blockquote type="cite"><div><div><div>The down side I can see is that it could encourage users to adopt a “context dictionary” approach with a bunch of keys rather than defining their own context type using a single key. This is generally a bad idea and should be discouraged.</div><div><br class=""></div><div>I think we should make the more common single-content use case more convenient and subtly nudge users in the right direction by making it easier to use. We could do this by defining a `DefaultContext` key, including a `defaultContext` property on `Encoder` and `Decoder` in an extension which returns `self.context[DefaultContext]` and encouraging encoders and decoders to provide an override that takes a single `context: Any` argument which gets placed in the context dictionary using that key.</div><div><br class=""></div><div>I still very much prefer the stronger guarantees offered by Brent’s first design but I could live with this.</div><div><br class=""></div><div>One last thought - might it be possible to restructure the design a way that would allow us to build type-safe context-awareness on top of existing encoders / decoders? I’m going to give a little bit of thought to this but don’t expect to come up with a good answer.</div><div><br class=""></div><div>At least one hurdle to this in the current proposal is that the Foundation encoders and decoders hide the types that actually do the encoding and decoding. There are probably very good reasons for this of course, but they also make it more difficult to layer functionality like context-awareness on top of them. We would have to resort to a hack of some kind to even attempt 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=""></div><div class="">- Tony</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class=""><div dir="auto" class=""><div class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div class=""><br class=""></div><div class="">- Tony</div><div class=""><br class=""><div class=""><blockquote type="cite" class=""><div class="">On Mar 17, 2017, at 5:56 PM, Matthew Johnson via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</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 Mar 17, 2017, at 6:15 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 Mar 17, 2017, at 3:35 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; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -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="">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.<span class="Apple-converted-space"> </span></div></div></div></blockquote><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;"><br class=""></div><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;">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></div></div></blockquote><br class=""></div><div class="">Oh, I see. Sorry, I missed that when I was looking at your design.</div><div class=""><br class=""></div><div class="">In practice, in my design, you would only need to manually pass a context to `encode(_:forKey:with:)` if the context was of a different type than `self`’s.<span class="Apple-converted-space"> </span></div></div></div></blockquote><div class=""><br class=""></div><div class="">Oh, I see. I missed that part of your design. I really like it with the shorthands. I’m fully on board with this being the right way to handle contexts now. I think Context should be in the basic Codable protocol. That leaves the question of what to do with NSKeyedArchiver and NSKeyedUnarchiver. I’m not sure what the answer is for those but it would be unfortunate to see the design compromised solely because of a requirement to interoperate with 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="">This would probably happen at module or subsystem boundaries. Imagine, for instance, that your FooKit module (for interacting with the<span class="Apple-converted-space"> </span><a href="http://foo.io/" class="">foo.io</a><span class="Apple-converted-space"> </span>web service) needs to encode a GeoKit.Location instance, but both FooKit and GeoKit need information from a context to encode themselves properly, and they use different context types. When FooKit encoded a GeoKit.Location, it could construct and pass a GeoKit context.</div><div class=""><br class=""></div><div class="">I believe that in your design, unless the FooKit context was a subtype of the GeoKit context, you wouldn't be able to get GeoKit.Location to do the right thing.</div></div></div></blockquote><div class=""><br class=""></div><div class="">Right. It was assuming only one context would be needed for an entire encoding / decoding process. I don’t know of use cases where one module could meaningfully provide a context to another module unless they were very closely related (i.e. built as parts of the same system) but maybe they do exist. Your design is able to accommodate this very well.</div><div class=""><br class=""></div><div class="">I made some compromises to try and diverge from the current proposal as little as possible while still solving the primary use cases I’m aware of. Now that I understand your design I think it has enough advantages that we should go in that direction. And we certainly should not go in the direction of something that requires Any.</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=""><br class=""></div><div class="">If that weren't the case—if you were encoding a type with a matching context, or with a `Void` context—you could use the two convenience methods, which would handle the context argument for you. So threading contexts would only be necessary in a relatively rare case.</div></div></div></blockquote><div class=""><br class=""></div><div class="">Yep, that’s very elegant!</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;"><br class=""><div class=""><span class="Apple-style-span" style="border-collapse: separate; font-family: Helvetica; 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 class="" style="font-size: 12px;">-- </div><div class="" style="font-size: 12px;">Brent Royal-Gordon</div><div class="" style="font-size: 12px;">Architechies</div></div></span></div><br class=""></div></div></blockquote></div><br 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;"><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></div></blockquote></div></div></blockquote></div><br class=""></div></div></blockquote></div><br class=""></div></blockquote><blockquote type="cite"><div><span>_______________________________________________</span><br><span>swift-evolution mailing list</span><br><span><a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a></span><br><span><a href="https://lists.swift.org/mailman/listinfo/swift-evolution">https://lists.swift.org/mailman/listinfo/swift-evolution</a></span><br></div></blockquote></div></blockquote></body></html>