I don’t really see the point of Codable being in the standard library either, honestly. I’m just suggesting it for the sake of symmetry.
That being said, why are these often implemented by separate types? What benefit does that provide?
I don’t really see the point of Codable being in the standard library either, honestly. I’m just suggesting it for the sake of symmetry.
That being said, why are these often implemented by separate types? What benefit does that provide?
A lot of times there's only a need to decode, say a config file, but no need to write it. Or, as a slightly different, personal example, in CoreXLSX I only implement Decodable for now, as decoding is much easier to implement. I don't want my library to ship an Encodable implementation if that's not well tested. (And testing whether an arbitrary generated .xlsx file is valid is hard).
And vice versa, there are "write-only" use cases, where decoding is not needed.
Yes, but why include a protocol composition type in the standard library? I can’t think of any others, and you could just conform to Decodable & Encodable without the type alias.
Except conformance to both Encodable and Decodable is extremely common, so the unified protocol has value. There are also algorithms that can operate on Codable instead of the separate protocols. Neither of those things apply to a unified TopLevelCoder protocol.
I recognize that conforming to both is extremely common, but what algorithms use both at once?
I’ve almost never used Encodable, but I use Decodable extensively. That being said, I don’t see why separate types are so common.
class XLSXCoder {}
extension XLSXCoder: TopLevelDecoder {…}
extension XLSXCoder: TopLevelEncoder {…}
None of the requirements are mutually exclusive, and you could declare conformance to either or both. It wouldn’t make testing harder, either: on the contrary, it could deduplicate configuration options.
Heck, plenty of Formatter classes in Foundation handle both encoding and decoding: DateFormatter, for instance.
In the JSONDecoder there's a case in the DateDecodingStrategy that allows the user to provide their own decoding closure (_ decoder: Decoder) throws -> Date. In the case of DateEncodingStrategy, that closure is (Date, Encoder) throws -> Void.
So using your examples, the JSONDecoder and JSONEncoder need to have separate configuration options because the closures are fundamentally different depending on which way you are coding, so need to be separate types. This might not be the case for all top level encoder/decoders, but it is the case for those in the standard library already.
(Apologies for the edit, I'm failing at typing tonight.)
I don’t follow. Why would having DateDecodingStrategy and DateEncodingStrategy under the same type be problematic?
If they named it DateStrategy, that would make some sense, but they didn’t.
JSONDecoder is so similar to JSONEncoder that it is actually declared in the same file, seemingly for the sake of sharing implementation details using fileprivate.
Persisting data between app runs, transfering things through the internet between two swift apps, deep copying...
I wouldn’t call those single algorithms, but fair enough. Still, it is one of the only top-level global type aliases in the standard library that is the same across all platforms.
Just to offer a little bit more background here: you're right that nothing will reasonably use both algorithms at once. It doesn't really make sense.
The main reason we added typealias Codable = Encodable & Decodable is that we wanted Codable to be the one-stop easy keyword to reach for. Although your use case might not require Encodable, the vast majority of types in existence benefit from confoming to both at once. The intent here was not to benefit writers of methods using Codable as a generic constraint, but instead, for writers of types conforming to Codable as the default.
The reason that Codable isn't a single protocol is exactly as you mention: the actual usage of these protocols is orthogonal, and it's very easy to have a type that just cares about encoding, or just cares about decoding. In that sense, seeing Encodable on its own, or Decodable on its own, should cause one to stop and think about this type and its usage (and in the same vein, you should consider whether it makes sense to only implement one or the other).
As for
@danielctull has it right:
I really wanted these to be a shared type, but the .custom strategy for these types has a necessarily different interface. We considered having a single .custom value containing both encode and decode closures, but as discussed above, not everyone cares about both encoding and decoding. (The last alternative was having a single .custom value with optional closures, but that's messy for its own reasons.) Hence, two separate types like everything else.
The current names (in the namespace of the module) are:
JSONDecoder.DateDecodingStrategyJSONEncoder.DateEncodingStrategy.Why couldn’t it be:
JSONDecoder.DateStrategyJSONEncoder.DateStrategyJSONCoder.DateDecodingStrategyJSONCoder.DateEncodingStrategyIt seems redundant.
On a related note, I think we can all agree that being able to share cases between enumerations would be amazing. I’m under the impression it would make no sense in terms of the type system, but one can dream. For now, we just have to accept a lot of duplication.
What is the sense in bikeshedding established names? I think this has gone way too far from the main topic at hand.
I concur, my mistake.
I had exactly the same thought when I initially read Brent's post for what it's worth.
Not sure if this is stale... are there any plans to advance this?
We've also introduced a custom protocol in RxSwift to support a similar decode operator but would prefer to use a Foundation/stdlib protoocol if that exists.
This would be more appropriate in Foundation.
That was my instinct as well; being in the same module as the concrete encoders and decoders.
Putting it in Foundation doesn’t really make sense to me. They’re intended as a generic abstraction for coders, alongside Codable. That Foundation contains Swift’s two actual coders is irrelevant, as that decision was made solely to reuse the existing Foundation serialization classes. Native serializers would naturally be part of the standard library, as they are in many other languages.
In any case, I don’t think it’s in the best interest of the community to place valuable APIs in Foundation, as any influence the community may have is then lost.
Bumping a six-year-old thread, but is there any chance we could actually do this? This is causing me some implementation headaches, and seems like such a slam dunk in terms of usefulness to effort ratio.