- Proposal: TBD
- Author(s): Yong hee Lee
- Status: TBD
- Review manager: TBD
This proposal submits a Swift language enhancement that creates anonymous enumerations for simple flag variables. Swift already supports anonymous functions (closures) and a restricted form of anonymous structs with tuples. This proposal introduces an anonymous approach for enumerations, enabling an anonymous variant for each fundamental entity in the language.
The Swift-Evolution discussion of this topic took place in the Anonymous Enums (Updated) thread. This proposal uses lowerCamelCase enumeration cases in compliance with current API Guideline Working Group guidance.
An anonymous enumeration is a flag variable type. It creates semantically rich Swift code without the overhead required to introduce extra types. This feature does not replace normal enumerations. An enumeration that's used in more than once place, has associated or raw values, or provides semantics beyond use as a flag is not a suitable candidate for this proposal.
Instead, anonymous enumerations capture simple flag states using a parsimonious syntax. They might replace Boolean flags so API consumers needn't translate the meaning of the flag to or from truth values or they might introduce a limited collection of possible values, such as colors or temperature levels.
Anonymous enumerations push semantics to the context of a call, letting that call use meaningful tokens. This produces streamlined, use-specific syntax both at the call site and in the method and function declarations where they are defined.
Anonymous enumerations:
- Streamline coding, eliminating standalone types that are used only once.
- Enable you to see all enumeration options at a glance.
- Encourage switch-case coding.
As you will see in the detail design, anonymous enumerations are a small difference that introduce a big change in Swift.
The proposed anonymous enumeration syntax combines one-use enumerations into a single group integrated into a method or function's type.
func adjustTemperature(temperature temp: [ low | normal | high ]) throws { ... }
A multi-line declaration might look like this:
func adjustTemperature(
device: DeviceType,
temperature temp: [ low | normal | high ]
) throws
Anonymous enumerations would be practically limited to 255 tokens and probably should not be used for more than two to five items in actual code.
Note: The square bracket syntax used here may need to change due to possibles conflict with collections.
At this time, one-use enumerations must be declared separate from the context that consumes them. Here is an example of a one-use Temperature
enumeration declared in current Swift.
struct MyTempStruct {
// Temperature type declared here
enum Temperature {case low, normal, high}
// Temperature type used here
func adjustTemperature(device: DeviceType, temp: Temperature) throws {
//...
}
}
Incorporating a one-time-use enumeration into a function or method streamlines code and reduces excess declarations. If adopted, the temperature enumeration from the preceding example is subsumed into the declaration, as shown here:
struct MyTempStruct {
func adjustTemperature(
device: DeviceType,
temperature temp: [ low | normal | high ]) throws {
//...
}
}
In the current version of Swift and Xcode, the Quick Help output for
adjustTemperature()
produces the following Declaration
line:
func adjustTemperature(device: DeviceType, temperature temp: Temperature) throws
Under this proposal, Quick Help updates to provide an instant reference for consumers. It incorporates the anonymous enumeration options directly into the calling pattern shown to the API consumer.
func adjustTemperature(device: DeviceType, temperature temp: [ low | medium | high ]) throws
The current version of Swift encourages if-statement consumption of Boolean values. For example:
var switchIsOn : Bool = true
if switchIsOn {
// ...
print("...on...")
// ...
} else { // no further context here other than "else"
// ...
print("...off...")
// ...
}
In contrast, anonymous enumerations lend themselves to switch statements.
switch switchState {
case .on:
// ...
print("...on...")
// ...
case .off:
// ...
print("...off...")
// ...
}
The result is cleaner, easier-to-follow coding. The switch statement provides
better readability, labeling each case for easy visual identification.
Switch statements push back against nested, complex if
-statement coding.
They produce better structured code where each branch is fully labeled
in contrast with unlabeled else
clauses.
Using collection syntax for enumeration flags may be problematic. It is left as an exercise for the mailing list and the Swift team to suggest possible alternatives such as:
func f (switchState: [ on | off ]) { ... } // current
func f (switchState: <on | off >) { ... } // angles
func f (switchState: (on, off)) { ... } // parens, commas
func f (switchState: ( on | off )) { ... } // parens, pipes
func f (switchState: {case on, off}) { ... } // comma-delim cases
func f (switchState: enum(on, off)) { ... } // enum syntax, commas
func f (switchState: enum( on | off )) { ... } // enum syntax, pipes
// ...etc...
It looks to me like this offers real benefits to the function being called but at a high complexity cost to the calling function.
I imagine I might call
struct.adjustTemperature(device, temperature: .low)
easily enough but what if I wanted to write:How would I express the type of
temperature
if the function takes an anonymous enum?Similarly suppose I, as the author of
MyTempStruct
, want to refactoradjustTemperature
and introduce a private helper methodfunc convertTemperatureToDegreesCelcius(Temperature: temperature) -> Double
. How can I declare that function to accept my anonymous enum. If the solution it to extract it back into a non-anonymous enum why should this internal refactor change the types in my public interface?