Skip to content

Instantly share code, notes, and snippets.

@erica
Last active February 22, 2016 23:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erica/9148e2be916c7fae6f1e to your computer and use it in GitHub Desktop.
Save erica/9148e2be916c7fae6f1e to your computer and use it in GitHub Desktop.

Ad Hoc Anonymous Enumerations for Simple Flag Variables

  • Proposal: TBD
  • Author(s): Yong hee Lee
  • Status: TBD
  • Review manager: TBD

Introduction

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.

Motivation

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.

Detail Design

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.

Streamlining Code

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 {
        //...
    }
}

Enumerations at a Glance

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

Switch Case Coding

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.

Alternatives Considered

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...
@jonah-williams
Copy link

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:

var device = ...
let temperature: MyEnumStruct.Temperature
if (...) {
  temperature = .low
} else {
  temperature = .high
}
struct.adjustTemperature(device, temperature: temperature)

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 refactor adjustTemperature and introduce a private helper method func 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?

@erica
Copy link
Author

erica commented Feb 22, 2016

@jonah-williams

Assuming you mean to relay the use of an anonymous enumeration, Swift being a type-safe language, you could not create struct.adjustTemperature(device, temperature: .low) because there is no temperature type. If you attempt to pass .low as an argument to anything, the compiler will throw an error. It would be trivial to add a further exclusion that prevents an anonymous enumeration being passed to an Any argument.

The second scenario could never come up as there's no Temperature type.

@jonah-williams
Copy link

@erica agreed, that type is missing. I mean to suggest that the absence of that type presents a major hurdle if anonymous enumerations were available to be used in an interface.

Consumer of an interface are constrained to passing in-line literal arguments for every permutation of anonymous enumeration values. Imagine if I gave you func adjustEnvironment(temperature temp: [ low | normal | high ], humidity: [ low | normal | high ], pressure: [ low | normal | high ]) and you tried to map some set of user input into calls to that method. Anonymous enumerations would make it easier to write difficult to use interfaces.

Providers of an interface using anonymous enumerations receive value typed arguments which are effectively @noescape'ed. These argument can never be directly passed on no matter how reasonable it would be to do so. Anonymous enumerations would make it easy to write interfaces which constrain their implementation.

I don't believe the benefit of quick enumeration declarations would be worth the cost anonymous enumerations would impose on a code base. Only the initial author would be well served by this feature at great cost to anyone who attempts to use the resulting interface in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment