> On Mar 17, 2017, at 2:38 PM, Matthew Johnson <[email protected]> wrote:
> 
>> 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.
>> 
>>      protocol Codable {
>>              associatedtype CodingContext = ()
>>              
>>              init<Coder: Decoder>(from decoder: Coder, with context: 
>> CodingContext) throws
>>              func encoder<Coder: Encoder>(from encoder: Coder, with context: 
>> CodingContext) throws
>>      }
>>      protocol Encoder {
>>              associatedtype CodingContext = ()
>>              
>>              func container<Key : CodingKey>(keyedBy type: Key.Type) -> 
>> KeyedEncodingContainer<Key, CodingContext>
>>              …
>>      }
>>      class KeyedEncodingContainer<Key: CodingKey, CodingContext> {
>>              func encode<Value: Codable>(_ value: Value,? forKey key: Key, 
>> with context: Value.CodingContext) throws { … }
>>              
>>              // Shorthand when contexts are the same:
>>              func encode<Value: Codable>(_ value: Value,? forKey key: Key) 
>> throws
>>                      where Value.CodingContext == CodingContext
>>              { … }
>>              
>>              …
>>      }
> 
> 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.

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.

> 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.

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:)`:

        class KeyedEncodingContainer<Key: CodingKey, CodingContext> {
                func encode<Value: Codable>(_ value: Value,? forKey key: Key, 
with context: Value.CodingContext) throws { … }
                
                // Shorthand when contexts are the same:
                func encode<Value: Codable>(_ value: Value,? forKey key: Key) 
throws
                        where Value.CodingContext == CodingContext
                {
                        try encode(value, forKey: key, with: currentContext)
                }
                
                // Shorthand when the type uses a Void context:
                func encode<Value: Codable>(_ value: Value,? forKey key: Key) 
throws
                        where Value.CodingContext == Void
                {
                        try encode(value, forKey: key, with: ())
                }
                
                …
        }

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.

(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.)

> Did you take a look at the design I suggested?  What do you think of it?

I think that, if a type wants to support context-free coding, it should use an 
optional `CodingContext`. :^)

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:

        public protocol ContextAwareCodable {
                associatedtype CodingContext
                
                init(from decoder: Decoder, with context: CodingContext) throws
                func encode(to encoder: Encoder, with context: CodingContext) 
throws
        }
        public protocol Codable: ContextAwareCodable where CodingContext == 
Void {
                init(from decoder: Decoder) throws
                func encode(to encoder: Encoder) throws
        }
        extension Codable {
                public init(from decoder: Decoder, with context: Void) throws {
                        try self.init(from: decoder)
                }
                func encode(to encoder: Encoder, with context: Void) throws {
                        try encode(to: encoder)
                }
        }

Most `Encoder`/`Decoder` APIs would have to use `ContextAwareCodable`, but if 
you're writing a coder, you'd better be aware of contexts.

* * *

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`.

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.

-- 
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to