> On Mar 19, 2017, at 3:38 PM, Brent Royal-Gordon <[email protected]>
> wrote:
>
>> On Mar 19, 2017, at 12:21 PM, Tony Parker <[email protected]
>> <mailto:[email protected]>> wrote:
>>>
>>> On Mar 19, 2017, at 12:14 PM, Matthew Johnson <[email protected]
>>> <mailto:[email protected]>> wrote:
>>>
>>> On Mar 19, 2017, at 10:47 AM, Tony Parker <[email protected]
>>> <mailto:[email protected]>> wrote:
>>>
>>>> Hi Matthew, Brent,
>>>>
>>>> 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).
>>>>
>>>> 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.
>>>
>>> Hi Tony. 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.
>>>
>>> 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.
>>>
>>> I much prefer the static safety but having a solution is better than not
>>> having one. :)
>>
>> The Any context property is reasonable, but it would be nice to find
>> something in the middle. =)
>>
>> 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].
>
> I hate casting out of Any, and I strongly believe we should support multiple
> contexts, so personally, I'd prefer something typed:
I can imagine multiple contexts being useful in rare cases; much more rarely
than a single context but still worth supporting.
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.
>
> protocol Encoder {
> // Retrieve the context instance of the indicated type.
> func context<Context>(ofType type: Context.Type) -> Context?
>
> // This context is visible for `encode(_:)` calls from this
> encoder's containers all the way down, recursively.
> func addContext<Context>(_ context: Context, ofType type:
> Context.Type)
What happens if you call `addContext` more than once with values of the same
type? And why do you require the type to be passed explicitly when it is
already implied by the type of the value?
> }
> // Likewise on Decoder
>
> // Encoder and decoder classes should accept contexts in their
> top-level API:
> open class JSONEncoder {
> open func encode<Value : Codable>(_ value: Value, withContexts
> contexts: [Any] = []) throws -> Data
> }
What happens if more than one context of the same type is provided here? 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.
I’m going to speculate that the intent above is that the types of the context
values are treated as a key into a dictionary, or something along those lines.
I’ll also speculate that if more than one context of the same type exist are
provided at a given stack level level the latest one overwrites the previous
one (what else would happen - `addContext` is non-throwing).
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.
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.
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).
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.
This seems to leave us with the options of having a single context, multiple
contexts accessed via an explicit key like Tony has suggested or the earlier
design you presented which the Foundation team feels has too much surface area.
The only other direction I can come up with that we might be able to explore is
if we could modify the design so it would be possible to build context-aware
coders and decoders on top of “basic" encoders / decoders. I haven’t had a
chance to think this through much yet but I suspect changes that would enable
this would also be unacceptable to the Foundation team.
>
> --
> Brent Royal-Gordon
> Architechies
>
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution