Getting rid of `case` keyword for enum definition?

It's definitely clear we are listing the elements whenever we are inside an enum

I consider case keyword superfluous

enum Error: Int {
    notInitialized = 0x00010001
    noCurrentContext = 0x00010002
    ...
}

What do you think?

3 Likes

You can also declare functions, compute properties, and static counterparts in enum. In fact, they could easily become more common than case declaration.

enum Foo {
  case a, b, c

  func doSomething() { ... }
  var someRelatedValue: String { ... }
  static func doSomethingElsr() { ... }
}
6 Likes

That would be too much convenience/sugar for my brain to parse. I would be against such a change.

20 Likes

I think this has promise. The premise - of reducing unnecessary verbosity - is solid. The question is whether dropping case does this without reducing clarity.

Conceptually it’s reasonable to think of the enum’s cases as being defining aspects of the enum, not attributes of the enum. So there’s room to consider distinct syntax.

Practically, I don’t see any obvious issues - re. confusion or ambiguity - vs properties or methods of the enum. It’s quite straightforward to learn & grok that an unadorned name inside an enum block is a declaration of one of the enum’s cases.

e.g.

enum Colour {
  red
  green
  blue
  indigo
  violet
  beige
  offwhite
  stucco
}

That looks very clean to me - nobody is going to be confused by what that means.

enum Planet {
  Mercury
  Venus
  Earth
  Mars
  Jupiter
  Saturn
  Uranus
  Neptune

  var thirdRockFromTheSun: Planet { Planet.Earth }
  func diameter() -> Double { ... }
}

I see no potential confusion between computed properties & methods. The distinction is that the cases are special - they don’t require a keyword adornment, unlike all other things you can attach to an enum. I think that’s reasonable and fair - the enum is first and foremost about its cases.

3 Likes

On the one hand, since we have function builders and their special DSLs using this kind of syntax, it seems reasonable.

On the other hand, I really dislike this reductionist philosophy that leads people to try and remove all syntax and keywords. The “case” keyword is pretty harmless IMO, and establishes a term of art (e.g. it’s maybe less clear what CaseIterable or allCases mean if you never write the word “case” when defining a case).

24 Likes

This "reductionist philosophy" has been there since tens of years in languages such as Java (or Kotlin), to say

So I'd say it has been deeply put on test

This is actually nothing new (or revolutionary)

2 Likes

This proposal is unlikely to go anywhere barring any compelling use case, such as enabling something like SwiftUI, which motivated dropping the return requirement in some cases. Removing keywords for the sake of removing keywords is never a good enough reason for modify the language.

13 Likes

It’d be insightful to know why @Chris_Lattner3 and the rest of the original Swift authors adopted the syntax we have, since it seems like it must have been an explicit choice, not merely convention at the time - many of the languages that strongly influenced Swift (Objective-C, C, & C++) don’t have any keyword for this purpose. Maybe Chris et al had good reason(s), as opposed to it being an accident, and those reason(s) might still stand.

Re. viability of this as a pitch… yes, convenience changes like this are unlikely to pass review, if only because they tend to die under the weight of bike-shedding, but if nothing else they can still be informative for future languages, and intellectually interesting. Many times now in these sorts of discussions in these forums I’ve learnt surprising and/or insightful reasons why seemingly accidental or arbitrary choices were made.

4 Likes

Just MHO:

Swift is a language that has leading keywords for several specific reasons: they are what attributes hang off of, they are what the syntactic indentation rules (usually) are built on top of, and they are also a point of unification for the overall statement and decl grammar.

I love enums and would love to make the trivial versions of them simple. The obvious option is `enum X { A, B, C }. The problem with this is that we don't have a way to make the trivial case general, and this isn't particularly consistent with the base grammar. Given that lots of things will push to the higher energy state (which is more general), this could be a significant fracture in the language.

That said, I was pretty opposed to implicit return keywords in functions (preferring the = syntax) and the sky hasn't fallen... :-)

-Chris

16 Likes

I personally don't feel the need to avoid writing case on each line. I also take note that you can already avoid writing a case on each line by using commas:

enum Planet {
  case
    Mercury,
    Venus,
    Earth,
    Mars,
    Jupiter,
    Saturn,
    Uranus,
    Neptune

  var thirdRockFromTheSun: Planet { Planet.Earth }
  func diameter() -> Double { ... }
}

That's perhaps not as clean as requiring neither case or commas, but I think it's important to mention it as an option that works today.

12 Likes

It may be worth being aware of some of the more interesting side effects you get today depending on which choice you make (nothing that changes semantics, though).

For example, if you want to attach doc comments to each of your cases in a way that the compiler will extract them and make them available to other source tooling (Xcode quick help popups, etc.), you must put each case in its own case declaration:

enum Foo {
  /// Description of bar
  case bar
  /// Description of baz
  case baz
}

Multiple values per case declaration don't work the way you'd expect:

enum Foo {
  /// This will be treated as the doc comment for .bar and .baz
  case
    /// This comment is ignored
    bar,
    /// This one is also ignored
    baz
}

Not a major issue by any stretch of the imagination (and probably something worth investigating), but it may affect what you choose to write in the present.

10 Likes

Isn't it something that can be fixed in the compiler rather than on the syntax level?

It could—the sole purpose of my post was just to show limitations that exist today with one syntax vs. another, not to argue in favor or opposition to language changes.

1 Like

I could see this working only if the rule was that all of the "Case" statements had to precede any thing else otherwise It will get concussing if you allow:

enum Planet {
  Mercury
  Venus
  Earth
  Mars
  Jupiter
  Saturn

  var thirdRockFromTheSun: Planet { Planet.Earth }
  func diameter() -> Double { ... }

  Neptune
  func distance() -> Double {}
  Uranus
  var something: String { }
}
6 Likes

it makes sense

Requiring all of the cases be listed first seems like the best compromise, but I'm not sure it feels right and is likely unprecedented in Swift. At that point, it might be worth considering that enum {} cannot have any vars or functions or anything else in the body and that you must only define those in extensions. That way the enum is purely about the actual enumerations that are possible - except this, too, would be pretty much unprecedented in Swift, I think.

(As I ponder this, I sort of like the idea of making enum, struct, and class more 'structural' so that you could only define the stored properties in the main declaration and all functions and initializers had to be defined as extensions. :stuck_out_tongue: Of course this would never fly at this point and probably has all kinds of problems I'm not thinking about.)

That’s very similar to how it worked in Objective-C. So it’s clearly possible and not the end of the world. But, it’s not a degree of flexibility to be given up needlessly, either.

I do think it’s valuable to consider the best practice ‘style’ aspects of enums in this discussion, since they’re tied to these value judgements re. allowing any order of the enum’s contents. I don’t often see enums’ cases declared in anything but one contiguous list - in any language. Maybe with some blank lines and/or comments inserted here or there, but I don’t think those count in this context.

I’m interested to hear if there’s any particular examples where contiguous case definition is not optimal. If it proves to be, then there’s no practical problem with a potential restriction that the enum cases must appear before any computed variables, constants, etc.

Nor, strictly speaking, does the compiler have to enforce that - it can just remain best practice.

I’m trying to think of an example of such a [future] attribute, but not coming up with anything - enum cases don’t really have a lot of individuality by design, other than their name and raw value. Even their storage type is a enum-level attribute. They’re not callable, they’re not storable or otherwise mutable, so that rules out the big two families of attributes in Swift today. I suppose conceivably they could have different access levels one day? e.g. this subset of values you as Joe Public may use, but these other ones can only be created within the package? I’m not sure I foresee a real use case there.

I’m not sure I see why a simple (if not common) case can’t be optimised for in a way that doesn’t detract from a more flexible case. e.g. even if it had to be that an enum containing anything other than case definitions had to use the case keyword as at present… so? That still means quite a lot of enum declarations can be shorter & sweeter. (about the worst I can come up with is that new users will ask why they have to suddenly add case prefixes to everything when they add e.g. a computed property, but IMO that merely suggests that the requirement to do so is problematic, not that the possibility to omit it is)

If we're at the point where you have to write all the enum cases up front and you possibly can't even have any computed properties or other methods, then you can already write enum X { case A, B, C } so I struggle to see the point of introducing inconsistency for almost no gain in brevity.

6 Likes

Access control and resilience are two simple examples of things you'd want to put on a case. 'indirect' is an existing declmodifier on them as well.

7 Likes
Terms of Service

Privacy Policy

Cookie Policy