This is some excellent feedback! One thing I wanted to respond to:
When @Morten_Bek_Ditlevsen and I discussed some of the topics here originally, this subject came up and I suggested that CodingKeyRepresentable
not have an associatedtype
— I wanted to share some of those reasons for posterity, and in case they might influence the decision of where to go here.
Because associatedtype
s have non-zero cost on the consuming side (e.g. checking for CodingKeyRepresentable
conformance, using the key type), I think that the associated type definition would need to carry its weight. Despite the name, I think that the key difference between CodingKeyRepresentable
and RawRepresentable
is that the identity of the RawValue
type is crucial to RawRepresentable
, but not so in the CodingKeyRepresentable
case.
On the consuming side of CodingKeyRepresentable.codingKey
(e.g. in Dictionary
), I don't believe key type identity is necessarily useful enough:
- The main use for the
.codingKey
value is immediate retrieval of the underlyingString
/Int
values.Dictionary
would either pull those values out for immediate use and throw away the original key - In a non-generic context (or even one not predicated on
CodingKeyRepresentable
conformance), you can't meaningfully get at the key type. The type-erasure song and dance you have to do to get the key values won't be able to hand you a typed key (and the pain of doing that dance is that because it doesn't make sense to expose a public protocol for doing the erasure, every consumer that wants to do this needs to reinvent the wheel and add another protocol for doing it; we had to do it a few times forOptional
s and it's a bit of a shame) - Even if it were necessary to get a meaningful key type, the majority use-case for this feature, I believe, will be to provide dynamic-value keys for non-enumerable types (e.g.
struct
s likeUUID
[though yes, we can't make it conform]); for these types, you can't necessarily define aCodingKey
senum
and instead, you'd likely want to use a more generic key type likeAnyCodingKey
(which by definition doesn't have identity)
On the producing side (e.g. in MyCustomType
), I'm also not sure the utility is necessarily enough: in general, the majority of CodingKeyRepresentable
types (I believe) will only really care about the String
/Int
values of the keys, since they will be initialized dynamically (again, I think of UUID
initialization from a CodingKey.stringValue
— you can do this from any CodingKey
).
I believe that the constrained MyKey
example above will be the minority use-case, but expressed without the associatedtype
constraint too:
enum MyKey: Int, CodingKey {
case a = 1, b = 3, c = 5
// There are several ways to express this, just an example:
init?(codingKey: CodingKey) {
if let key = codingKey.intValue.flatMap(Self.init(intValue:)) {
self = key
} else if let key = Self(stringValue: codingKey.stringValue) {
self = key
} else {
return nil
}
}
}
struct MyCustomType: CodingKeyRepresentable {
var useB = false
var codingKey: CodingKey {
useB ? MyKey.b : MyKey.a
}
init?(codingKey: CodingKey) {
switch MyKey(codingKey: codingKey) {
case .a: useB = false
case .b: useB = true
default: return nil
}
}
}
I personally find this equally as expressive, and I think that not requiring the associated type gives more flexibility without a significant loss, especially with non-enum
types in mind. What do you think? (If MyCustomType
here was inspired by a real-world example, I'd love to know more about it!)