SE-0295: Codable synthesis for enums with associated values

I’ve been looking at common practices some more, and there does indeed seem to be a clear split between ’case as value’ and ’case as key’.

One thing that stands out to me is that many of the ’case as key’ examples only have single unlabeled associated values, e.g. Either<A,B> or IntOrString. It makes perfect sense to me that if this is how you mostly use associated values, then it’s more intuitive to encode the case as a key. Whereas if your associated values mostly have labels, and sometimes even overlap between cases, maybe it’s more intuitive to encode the case as a value.

That makes me wonder if a workable compromise could be to divide synthesis into different cases (no pun intended):

A. Enums with single unlabeled associated values

These would be encoded using the case as the key, and the associated value as the value, i.e. not wrapped in a keyed or unkeyed container.

enum IntOrString: Codable {
  case int(Int)
  case string(String)
}

is encoded as

{
  "int": 3
}

{
  "string": "Hello"
}

B. Enums with labeled associated values

These would be encoded using the case as a value, and the associated values in the same container.

enum Command: Codable {
  case load(key: String)
  case store(key: String, value: Double)
}

is encoded as

{
  "$case": "load",
  "key": "MyKey"
}

{
  "$case": "store",
  "key": "MyKey",
  "value: 42
}

C. Enums with multiple unlabeled associated values, or both labeled and unlabeled associated values

These wouldn’t get synthesis. Synthesis is only a convenience after all, and if there’s no obviously correct behavior the best thing is to write the conformance manually.


This seems to me like it might be a good compromise that gets us most of the benefit of synthesis with the least surprising behavior for both camps.

It would also avoid a lot of the complexity of both the proposal and the counter proposal: There’s no question of what to do with mixed labeled and unlabeled associated values. And we don’t need a way to organize and name separate coding keys for each case.

Finally, there’s an easy way to switch between the two encodings, without extra protocols or attributes:

  • Want behavior A but with labeled associated values? Wrap them in structs.

  • Want behavior B but with single associated values? Give them labels.

3 Likes