Extensible Enumerations for Non-Resilient Libraries

Thank you! If nothing else, the final reason lays the matter to rest with respect to this thread. I have some remaining musings floating around in my head about associated values and structs and what exactly is the nature of this power that currently only enums provide, but I'll bring that elsewhere.

It's all about giving package maintainers more tools to express the concepts embodied by their API. I'm not sure it's helpful to say some usage is correct, and other usage is actually a "misuse of enums".

Some packages will have values which can be in 1 of n states, and each state may have a different associated value, but the set of states should be allowed to change over time and, in the package author's judgement, it isn't necessary for clients to handle every single state. There is a degree of independence between each state which makes it valuable to understand some of those states without needing to understand all of them.

Other values may require exhaustive handling, and the package author may decide that it should require a (disruptive) semver-major increment when a new state is introduced.

Is it feasible to distill the essence of the alternative design and show in code what it is that the proposed feature would alleviate?

Again, as far as I understand now, this is basically an obvious hole in an otherwise continuous fabric of available features, so I'm not arguing against adding it, I'm just asking out of my curiosity about Swift API design and its various pitfalls and solutions.

One problem I'm currently working on that made me stumble in this post is trying to extend an existing non-frozen SDK enum with more capabilities provided by my package.

Let's say an existing SDK has a hypothetical non-frozen enum that represents available video quality options:

enum VideoQuality: Int {
    case low, medium, high
}

My package exposes an enum with more options and uses VideoQuality as a private implementation detail:

public enum MyVideoQuality: Int {
    case low, medium, high
    case quality4K = 1000
    case quality8K = 1001
    case quality16K = 1002

    private var sdkVideoQuality: VideoQuality? {
        VideoQuality(rawValue: self.rawValue)
    }
}

I want to internally make low, medium and high behave as passthrough and exactly map to the SDK counterparts, and my additional cases to have custom behavior. I also want to support potential future cases added by VideoQuality. So I want my package to allow clients to add @unknown default when switching over my cases the same as they would do if they were switching over the SDK VideoQuality enum.

From my understanding, this proposal handles my case perfectly.

I didn't see my specific case mentioned here, so I wanted to share it, whether extending existing non-frozen enums is a good or bad use of this proposal is a question I'm still evaluating.

1 Like

if we are being honest, i personally tend to make a lot of things enums that in hindsight should not have been enums because:

  1. i can destructure enum associated values with pattern matching in a switch statement, but i can’t match structures the same way.

  2. typing @inlinable public static var <case name>:Self { .init(_uncheckedRawValue: <case value>) } over and over again gets tiring.

  3. i am writing an Error-conforming type, and i was too lazy to name and create a dedicated error type for each case of the error enum.

i am inclined to believe many other justifications for enum-ifying things are just rationalizations.

1 Like

There are many situations where, regardless of how 'unbounded' the possibilities are, we want to deal with a finite set of options which we might expand in the future.

A recent example for me is a 'step in an exercise'. Right now, there are 3 possible actions that can be a single step. I absolutely want an enum to let the compiler help me manage the complexity of 'how many paths are there?' and that list will probably expand with time if things go well.

Especially because restructuring and exhaustivity checking are exclusive to enums, it is–in my opinion–reasonable to use enums to represent concepts other than "well-known finite sets". Leading dot syntax is, basically, the last perk that I care about from enums. It's all about exhaustivity checking. All of which is to say that I still find

the most compelling way to address the issue