I got an idea of "enum var field" feature. Which is something like this.
enum Foo {
var bar: Int
case qux(String)
case kaz(fred: UInt8)
var waldo: Double
}
Which is just a shorten form of;
enum Foo {
case qux(bar: Int, String, waldo: Double)
case kaz(bar: Int, fred: UInt8, waldo: Double)
}
Or,
struct Foo {
var bar: Int
var _middle: _Middle
enum _Middle {
case qux(String)
case kaz(fred: UInt8)
}
var waldo: Double
}
Please note that all fields are positioned in order they appeared.
As the field exists for all cases, it can directly be accessed using property access syntax.
let a: Foo = ...
print(a.bar) // Okay.
a.bar = 333 // Sets `bar` field regardless of whatever case.
This kind of "fields repeated for all cases" sometimes happen, and repeating them for all cases is really painful if there're many cases. I think this feature would make programming in Swift a bit nicer.
Please point me if this topic has been discussed before. I want to know the conclusion.
When I was discovering Swift I was really confused, why can't I access an enum property if it is presented in each case and had same type so I had to dance around creating computed properties, switching over self to finally get my property.
I would like to have such ability to introduce a variable that will be presented in each case or compiler might synthesize it from the enum.
That`s the approach I took to meet my needs. But thinking about an enum which has cases with exact same name and type I wondered if I could have some shorthand to get it without switching over.
Still it might be more appropriate to use product types instead of trying to make sum types to behave like them.
I hit this problem a while ago and didn't have a good solution for it. I was parsing text, and each "Token" I got out of the string had a Range<String.Index> to indicate where in the string it came from. However, it also wanted to hold the various "kind" information about the token; was it an identifier, an operator, a literal, etc.
I ended up with this composite struct approach, but frankly it was exceptionally tedious to deal with. I wanted to switch over the returned token type, and that was difficult to do, because I would have to implement a custom ~= operator, or just switch on a property within the struct, which felt semantically incorrect. I'm not totally sold on "enums should allow stored properties" being the solution, but the enum-inside-a-struct approach ended up being a boxing nightmare.
FWIW, the code I was doing this on is here: GitHub - davedelong/DDMathParser: String → Number. There are several different layers of parsing, and pretty much each one deals with the enum-inside-a-struct approach, and pretty much each one ended up being more work than I felt was necessary.
If I were to rewrite my code, I might do tokens as a protocol type. Lately I've been warming up to the idea that enums are usually the wrong construct to be using, and I've been investigating ways to accomplish the same functionality (but subjectively "better") using protocols and class clusters.
However, a lot of my investigation into this area is impeded by the myriad of problems around protocols with associated types, the lack of decent built-in type erasure, or the utter pain of trying to implement a class cluster in Swift.
I can relate to wanting Enum syntax to be easier, but I think this proposal would be confusing in practice.
At the risk of being a broken record, about a dozen problems with payload Enums would go away if we just did the obvious (well, maybe "obvious" is my frustration speaking) thing and gave them Tuple syntax.
let mycase = MyEnum.foo(123,bar:"hello")
let a = mycase.0 // 123
let b = mycase.bar // "hello"
How would the compiler know that bar exists? Would all the cases have the same types in order? The only type information there is the enum type, and every case's associated types would need to be the same to guarantee that bar is there, but it could be done in that scenario. I don't think that syntax would work if the types were different across cases though.
Well, the obvious answer to that is to synthesize var bar: String?, which is nil unless the case has a bar "field". (And hopefully it could be non-optional if all cases had the field.)
But the other problem is, what would be the type of bar (or .0!) if your enum was
enum E {
case a(String, bar: Int)
case b(Int, bar: String)
}
?
Perhaps the synthesis would only be provided if there was no conflict like that (like the restrictions on Equatable synthesis).