Skip to content

Instantly share code, notes, and snippets.

@griotspeak
Last active January 12, 2018 23:36
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 griotspeak/963bc87a0c244c120264b11fb022d78c to your computer and use it in GitHub Desktop.
Save griotspeak/963bc87a0c244c120264b11fb022d78c to your computer and use it in GitHub Desktop.
Enum Subsets

This is loosely related to but not meant to 'compete' with the ad hoc enum proposal.

Introduction

This proposal adds/creates syntax to allow ad hoc creation of enums whose members are strict subsets of explicitly defined enums.

Swift-evolution thread: Discussion thread topic for that proposal

Motivation

Consider a situation where we have an enum Color which represents the entire set of colors relevant to your application with many salient methods and operations. We have also declared an enum LCDColorModel with only three colors, red, blue, green .

enum Color {
	case red, orange, yellow, green, blue, indigo, violet
	
}

enum LCDColor {
	case red, green, blue
}

The cases in LCDColor in our scenario do not require different behavior from their similarly named cases in Color. We would like, simply stated, to explicitly restrict the cases allowed within a specific portion of our software. There are, currently, a few approaches 1. Duplicate functionality in LCDColor - Completely manually - Protocols with 'minimal' manual duplication 2. Avoid duplication by allowing conversion to Color.

Neither of these solutions make the subset relationship between Color and LCDColor clear or strict.

Proposed solution

Add syntax to describe a restricted set of cases from an enum.

typealias LCDColor = Color.(red|green|blue)

LCDColor has all of the type and instance methods of Color.

  • Barring any technical reason, the 'actual' name of the type, in this example, is Color.(red|green|blue) This makes the relationship to Color syntactically clear. If a typealias is not desired, Color.(red|green|blue) should refer to the same type as LCDColor
  • Switching over Color.(red|green|blue) should only need to be exhaustive for the three cases .red, .green, and .blue.
  • Two initializers should be implicitly created
    • Color to LCDColor?
      • returns nil for all cases not in LCDColor
    • LCDColor to Color
      • Obvious and trivial implementation mapping cases from LCDColor to Color
  • Casting should be allowed
    • from superset to subset only using as? or as! syntax.
    • from subset to superset using as
  • Creating subsets of subsets is not allowed but reasonable conversions among subsets should be allowed if technically feasible such that:
    • Given subsets of C A and B, where A is a superset of B, the casting relationship between A and B should be similar to that between C and either of the other two named subsets.
  • Return types and types associated with protocol conformance should be contravariant. In this way Color.(red|green|blue) is not a full fledged type. It cannot have its own conformance to protocols which varies from its superset. eg:
enum Color : _Incrementable {
	case red, orange, yellow, green, blue, indigo, violet

	var successor: Color {
	    switch self {
	      case .red: return .blue
	      case .blue: return .green
	      case .green: return .red
	    }
  }
}

enum

let x: Color.(blue|green) = Color.blue
let color = x.nextColor  // type is `Color` without narrowing. 

Detailed design

While I am unsure of the entirety of the design, I propose that name mangling be used which, along with the declaration order restriction should mean that all possible subsets have a stable and predictable name which contains all of the information necessary to infer cases. If a mangled name approach is taken, the ordering of cases should be sorted to ensure stability.

Alternatives considered

  • Do nothing. This feature is not strictly necessary but does allow for expressivity not currently available in the language.
  • implicitly create properties which convert to superset type.

Impact on existing code

This is an additive change which should have no breaking change to existing code.

@mackoj
Copy link

mackoj commented Jan 12, 2018

Hi @griotspeak I have this need on some of my project do you feel like re-submit this evolution proposal ?

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