[swift-evolution] [Review] SE-0166: Swift Archival & Serialization

Brent Royal-Gordon brent at architechies.com
Wed Apr 12 19:44:22 CDT 2017

> On Apr 12, 2017, at 11:44 AM, Russ Bishop via swift-evolution <swift-evolution at swift.org> wrote:
> * String and Int keys being optional, giving a CodingKey the opportunity to return nil for both at runtime.

This was true in the first public draft, but I believe in this version `stringValue` is no longer Optional.

> * Encoder has three functions, but only one may ever be called. This API is the opposite of "pit of success".

Personally, I'm worried about that too. But I had a lot of trouble coming up with an alternative that didn't violate a more important goal, like "being able to throw errors" or "being compatible with classes".

Last-ditch suggestion: Change a bunch of names so that, for instance, `Encoder` is instead `EncodingContainerizer`. (That's a terrible name, but "Factory" gives me the hives.) That way, the name of the type gives you a hint that you're supposed to use it to make a container. You might even rename the methods to e.g. `useContainer(keyedBy:)`, which sound a little more stateful and mutually-exclusive.

	func encode(to encoder: EncodingContainerizer) throws {
		var container = encoder.useContainer(keyedBy: CodingKeys.self)
		try container.encode(name, forKey: .name)
		try container.encode(birthDate, forKey: .birthDate)

> I don't understand why KeyedEncodingContainer needs all those overloads; automatic conformance to Encodable should be provided for the stdlib types in those overloads so why would they be necessary?

I argued about this pretty vigorously. They want to avoid the overhead of building an encoder and single-value container and then making several generic calls to encode the value into the container. Personally, I think this is premature optimization of the highest order—particularly since building an encoder and single-value container are often no-ops—but this is the design they chose, and I don't think it's worth rejecting for that alone.

> KeyedEncodingContainer.encodeWeak seems like it should be a protocol refinement so you can check for the capability (or potentially know at compile time).

This probably ends up duplicating not only `KeyedEncodingContainerProtocol`,  but also `UnkeyedEncodingContainer`, `Encoder`, and `Encodable`. Doable, yes. Worth it, especially when `encodeWeak` has a pretty sensible fallback behavior? Eh.

> (One minor bit of bike shedding: decode/decodeIfPresent could instead be decode(required:) and decode(optional:)).

I don't think `required:` and `optional:` are really helpful. The name here is kind of like `addingWithOverflow(_:_:)`—you really want it to be `adding(x, y, .withOverflow)`, but it's not worth creating a dummy enum just to make a method read properly. Similarly, you'd like `decode(Int.self, .ifPresent)`, but the dummy's not worth the better readability.

I'm not sure why we don't do this, though:

	// Just showing Unkeyed for simplicity
	func decode<T: Decodable>(_ type: T.Type) throws -> T
	func decode<T: Decodable>(_ type: Optional<T>.Type) throws -> T?

	let alwaysInt = try container.decode(Int.self)
	let maybeInt = try container.decode(Int?.self)

> I really strongly dislike mixing up the Unkeyed and Keyed concepts. A type should need to explicitly opt-in to supporting unkeyed and that should be enforced at compile time. Unkeyed encoding is a potential versioning nightmare and should be handled with care.

I think they've done a reasonable job of putting unkeyed coding in a sharps drawer by making you specifically ask for it and giving it an ugly name. 

> C#'s serialization attributes are a better and more comprehensive solution but we don't have custom attributes in Swift and property behaviors were deferred. This problem is too important to leave to the future though. If we did ever add custom attributes or if property behaviors get implemented then this design could adopt them incrementally without breaking compatibility (e.g. a serialization transformer behavior that turns a non-Encodable property into an Encodable one, or a behavior that ignores a property for serialization purposes).

On the contrary, I think we *can* safely leave this to the future. If a future version of Swift and Foundation added:

	@uncoded var foo: Bar

That would be a completely additive change. Until then, there's an irritating but serviceable solution available—write your own CodingKeys enum and let code generation write the `Codable` conformances based on it.

Brent Royal-Gordon

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170412/de82df09/attachment.html>

More information about the swift-evolution mailing list