I was thinking about this last night, and also somewhat split on whether getting to a point where decoding Never is a logic error or a data error â and I while I do think can be either, I think that throwing an error is the right way to go here, for practical reasons.
Within the original context of the pitch, consider a type
enum E<A: Codable, B: Codable, ..., Z: Codable>: Codable {
case a(A), b(B), ..., z(Z)
}
(e.g., Either, or a similar generalization of it; not all cases need to have an associated value, just one where you might want to use Never)
I can easily imagine that code-wise, it may be desirable to pass around E<..., Never, ...> when you expect one of the cases to never be present under normal circumstances, and for well-formed data to never contain such a case. It's not a logic error, necessarily, to express this.
If we were to fatalError:
- Adversarially: it would be trivial to pass in a case which parses successfully, and causes the decode of a
Never, leading to an unrecoverable crash. The type decoding E wouldn't easily be able to prevent parsing and decoding this case, nor would E be responsible for checking for Never and preventing decoding it. (I can imagine the type containing E to also be generic over the type, meaning that you may, from some very far away point, need to deeply inspect the data just to ensure a specific case isn't present)
- Less adversarially: data changes over time, and what's disallowed now may not be in the future. An app may choose to use
E<..., Never, ...> today, but a future version of the app may instantiate that case â and if so, its encoded data may validly encode this case. If we tried opening this data up with an older version of the app, we'd crash with no recourse
I think that in both cases, it's better to throw an error so code can recover. That being said, maybe it's worth threading the needle by introducing a new DecodingError value (possibly underscored?), so that existing code at least won't be poised to catch it by name, and ideally it'll bubble up to the top, uncaught...