> RawRep is two things: a failable initialiser, and a getter. > When the compiler synthesises them, it generates those > exact same switch statements I proposed before. It’s quite > simple, and there are potential issues to allowing arbitrary > structs to use the same system. For one thing, they don’t all > have a 1:1 equivalence relationship, so it’s possible for multiple > enum cases to match the same raw struct. In that case, the order in > which the compiler synthesises its checks is important, but it’s quite > a hidden implementation detail that you wouldn’t be able to debug easily. > It’s better to ask the user to implement the RawRep requirement > themselves in those cases; it’s not a lot of work.
I'm aware of what is a basic idea behind RawRep. But it seems that I had a wrong understanding of how rawValue's are managed. I was not aware of the fact, that rawValue is created 'on demand' every time you use .rawValue property on enum case. What it means, is that if you are using a struct as a rawValue it is recreated from a string every single time you are accessing rawValue. Not cool :) Taking that into account I would consider changing the approach to somethin more like (http://swiftlang.ng.bluemix.net/#/repl/57febc248ef62b25bcea2a8a) enum Format { struct FormatStruct { var width: Int = 0 var height: Int = 0 private init(width: Int, height: Int) { print("GENERATE") self.width = width self.height = height } internal static let SMALL = FormatStruct(width: 30, height: 30) internal static let MEDIUM = FormatStruct(width: 60, height: 60) internal static let LARGE = FormatStruct(width: 120, height: 120) } case SMALL case MEDIUM case LARGE var size: FormatStruct { switch self { case .SMALL: return Format.FormatStruct.SMALL case .MEDIUM: return Format.FormatStruct.MEDIUM case .LARGE: return Format.FormatStruct.LARGE } } var width: Int { return size.width } var height: Int { return size.height } } I have to admit I was not expecting that. I thought, enum case rawValues are managed similar to statics and only referred to by enum cases. That was also a reason why I was convinced what 'storing' values in properties could be achieved as a variation of rawValue. Taking all of that into account I could agree that this proposal is more a syntax update than a new language feature. Storing additional properties would mean 1. store values as a part of enum case = extending required memory (like it is done in case of associated values) 2. force compiler to generate a rawValue struct implementing RawRepresentable protocol > What I meant by that is that you want convenience accessors for extra stuff > you want to put in the enum’s payload (where the associated values are). > I don’t think those values belong in the enum’s payload at all. I never wanted to put anything in enum payload. > What you propose looks dangerously close to the syntax for associated values, > IMO. A cleaner version might look like (strawman syntax): No. It does not look event similar to what you described. -- | Mateusz Malczak +------------------------------- 2016-10-12 21:20 GMT+02:00 Karl <[email protected]>: > >> On 12 Oct 2016, at 18:44, Mateusz Malczak <[email protected]> wrote: >> >> I got a bit confused, I think we are here talking about completely >> different features. Lets take a look at example from Karl >> >>> enum Something { >>> case oneThing(UIView) >>> case anotherThing(Error) >>> case yetAnotherThing(Int) >>> } >>> >>> …which is dangerously similar to Braeden’s example, really does store an >>> instance of UIView or Error or Int along with it. The size of the value is >>> the maximum of those, plus a couple of bits > to record which case it is >>> and what the type of the payload is. >> >> This is something we already have - and example of enum with >> associated values attached to enum cases. It has nothing to do with >> this proposal. > > Yes, I know what it shows. I’m saying that your proposed syntax looks > dangerously close to what we already have (IMO), but means something entirely > different. > >> >> The proposal is about being able to add stored immutable properties to enum >> type >> (not to enum cases as it is in case of associated values). Just like you can >> store properties in structs. >> > > If the properties are: > - immutable, and > - tied to specific enum cases > > then there is no need for them to be stored. You are asking to produce a > hard-coded value based on the value of the enum - basically to wrap a switch > statement. > We don’t need to store copies of numbers that are already baked in to your > source code. > >>> I think he wants convenience accessors for the associated values; the >>> problem is that in his example, he shouldn’t even be using associated values >>> at all. >> >> No, you are wrong, as I wrote its not even about associated values nor >> about accessors. >> > > What I meant by that is that you want convenience accessors for extra stuff > you want to put in the enum’s payload (where the associated values are). I > don’t think those values belong in the enum’s payload at all. > >>> You can already use structs as raw types. You just have to implement the >>> RawRepresentable conformance yourself. >> >> I know. And this has been also already discussed. Please see a >> previously discussed example here: >> https://swiftlang.ng.bluemix.net/#/repl/57fbf08a4f9bcf25fdd41634 >> >> And one more example with UIView stored in enum cases >> http://swiftlang.ng.bluemix.net/#/repl/57fe67ad4bb9da26d5438387 >> >> The only problem here is an amount of boilerplate code you need to >> create and limitations caused by using ExpressibleBy(*)Literal. >> > > RawRep is two things: a failable initialiser, and a getter. > When the compiler synthesises them, it generates those exact same switch > statements I proposed before. It’s quite simple, and there are potential > issues to allowing arbitrary structs to use the same system. For one thing, > they don’t all have a 1:1 equivalence relationship, so it’s possible for > multiple enum cases to match the same raw struct. In that case, the order in > which the compiler synthesises its checks is important, but it’s quite a > hidden implementation detail that you wouldn’t be able to debug easily. It’s > better to ask the user to implement the RawRep requirement themselves in > those cases; it’s not a lot of work. > > Most of the time, when you’re talking about things more complex than a number > or string, you only care about going from enum -> value. It’s much rarer that > you want to take a complex struct and condense it down in to a single enum > case. > >>> I agree with Karl on this. There should be clarity here as to what's >>> proposed. >>> If it's a change to how enums are laid out in memory, then you'll need to >>> show we're not sacrificing performance/size in the overwhelmingly more >>> common use cases, and why the extra storage is useful in the first place; >>> if it's syntactic sugar, that has already been proposed multiple times and >>> really isn't in scope for this phase of Swift 4--nor does it really enable >>> any new use cases not possible now. >> >> I agree when it comes to memory and performance, but at the same time >> we do not have to focus on how it should be laid out in memory. This >> is already solved. It is now possible to create enumeration type with >> custom structs as a rawType (see examples above). Enumeration type with >> stored properties could be handled in exactly the same way. Taking >> this into account I don't see any danger in terms of memory or >> performance hits. >> >> In my opinion it's also not a syntactic sugar. >> >> Lets get back to the previously discussed example >> >> We are discussing here a enumeration type defined as >> enum RectSize >> { >> let height:Int >> let width:Int >> case small(width: 30, height: 30) >> case medium(width: 60, height: 60) >> case large(width: 120, height: 120) >> } >> where >> var size: RectSize = .small >> size.height == 30 // true >> size.rawValue // Error: RectSizes has no property `rawValue`. >> size.height = 40 // Error: `height` is immutable >> print(size.height) // output: 30 >> >> >> There are at least two ways of this could be done. >> >> First solution would be to implement this as a new feature next to >> already existing two (enum with associated values, enum with >> rawValue). I don't have any detailed implementation in head. > > As I explained, "enum with rawValue” isn’t a feature. It’s a computed > property - either one you implement yourself, or synthesised by the compiler > for a couple of basic cases. > What you want is a computed property, but you want to write the values near > the cases instead of in a switch statement because they’re ugly. The compiler > will have to use those labels and synthesise the switch statement for you, > like it does for RawRep. > > What you propose looks dangerously close to the syntax for associated values, > IMO. A cleaner version might look like (strawman syntax): > > enum RectSize { > @property(width, 30) > @property(height, 30) > case small > > @property(width, 60) > @property(height, 60) > case medium > } > > But that looks even uglier than having a switch statement. So then you think, > what about if you could move those declarations elsewhere? maybe group them > to avoid redundant typing? > > enum RectSize { > case small > case medium > > @property(width: Int) > small = 30 > medium = 60 > @endproperty > > ..etc > } > > and presto! You have basically reinvented the switch statement in a computed > property. > >> >> Second approach would be to use struct as a rawValue. This feature is >> already available. >> >> struct UnderlyingRectSizeEnumStruct { >> var width: Int >> var height: Int >> init(width: width, height: height) { >> self.width = width >> self.height = height >> } >> } >> >> enum RectSize: UnderlyingRectSizeEnumStruct >> { >> case small(width: 30, height: 30) >> case medium(width: 60, height: 60) >> case large(width: 120, height: 120) >> } >> >> >> I hope this explains what was the initial idea behind this proposal. >> >> -- >> | Mateusz Malczak >> +------------------------------- >> >> 2016-10-12 17:07 GMT+02:00 Karl <[email protected]>: >>> Not at all - his proposal looks like the enum cases have an associated >>> value, when that is exactly what you _don’t_ want. It’s wasted storage >>> because they have a handful of known values. >>> >>> It’s a PITA, I get it, I go through it all the time, too; but really this >>> is the very definition of a computed property. There is nothing to be >>> stored here. Meanwhile, something like: >>> >>> enum Something { >>> case oneThing(UIView) >>> case anotherThing(Error) >>> case yetAnotherThing(Int) >>> } >>> >>> …which is dangerously similar to Braeden’s example, really does store an >>> instance of UIView or Error or Int along with it. The size of the value is >>> the maximum of those, plus a couple of bits to record which case it is and >>> what the type of the payload is. >>> >>> Confusing those things just because you don’t like writing switch >>> statements would be bad, IMO. It’s not that much code, and once you’ve done >>> a few of them you can make it quite compact. If you have a boatload of >>> associated values, wrap them in a struct. >>> Some more convenient generated accessors for the associated data might be >>> nice, but that’s been proposed and discussed to death. Others who have >>> followed the lists more closely can maybe tell you why we don’t have an >>> accepted proposal for it. >>> >>> - Karl >>> >>>> On 12 Oct 2016, at 13:52, Rien <[email protected]> wrote: >>>> >>>> I read Braeden’s example such that this proposal is in reality “just” >>>> syntactic sugar. AFAIAC it does not change how enums are implemented after >>>> compilation. >>>> >>>> Like sugar, it makes working with enums clearer and therefore easier. All >>>> initialisation values are defined right there with the ‘case’ instead of >>>> hidden in a tree of multiple ‘var’s. >>>> While I was sceptical about this proposal, Braeden’s example makes it a +1. >>>> >>>> Rien. >>>> >>>> Btw: I made the almost identical suggestion you did ;-) >>>> >>>> >>>>> On 12 Oct 2016, at 13:31, Karl <[email protected]> wrote: >>>>> >>>>> I very much disagree with the proposal, and all of the things supporting >>>>> it (like deriving enums from other types and whatnot). I think you need >>>>> to take a step up from caring about whether it’s a struct or enum and >>>>> think about what you are trying to model; that will guide you towards the >>>>> correct type(s) to use. >>>>> >>>>> You have only shown a handful of fixed size values, so I would suggest a >>>>> computed property in your case: >>>>> >>>>> enum FixedSize { >>>>> case small >>>>> case medium >>>>> case large >>>>> >>>>> struct Size { let width : Int; let height: Int } >>>>> >>>>> var size : Size { >>>>> switch self { >>>>> case .small: return Size(width: 30, height: 30) >>>>> // … etc >>>>> } >>>>> } >>>>> } >>>>> >>>>> There is no need for these sizes to be stored at all. If you want them >>>>> baked in to your enum’s values, clearly you expect them to be specific >>>>> values. It’s more efficient to just drop the stored data altogether in >>>>> this case; this enum will get lowered in to single byte, which is more >>>>> efficient to store and transport. >>>>> >>>>> Karl >>>>> >>>>>> On 12 Oct 2016, at 13:15, Mateusz Malczak via swift-evolution >>>>>> <[email protected]> wrote: >>>>>> >>>>>>> Mateusz, you lost me with “store some extra data”. >>>>>>> Does that mean something extra besides the code that Braeden suggested? >>>>>> >>>>>> What I meant by “store some extra data” was, to be able to define >>>>>> immutable properties of any type, as a part of enum instead of >>>>>> defining getters witch switches. I think Braeden example explains the >>>>>> whole idea of that proposal. >>>>>> >>>>>> -- >>>>>> | Mateusz Malczak >>>>>> >>>>>> >>>>>> 2016-10-12 8:42 GMT+02:00 Rien <[email protected]>: >>>>>>> I’d give a +1 for the suggestion of Braeden. >>>>>>> >>>>>>> Mateusz, you lost me with “store some extra data”. >>>>>>> Does that mean something extra besides the code that Braeden suggested? >>>>>>> >>>>>>> Rien. >>>>>>> >>>>>>>> On 12 Oct 2016, at 00:13, Mateusz Malczak via swift-evolution >>>>>>>> <[email protected]> wrote: >>>>>>>> >>>>>>>> That's exactly what this proposal is about. I would like to >>>>>>>> keep all enum properties but add an extra feature, so that enums can >>>>>>>> store some extra data. >>>>>>>> -- >>>>>>>> | Mateusz Malczak >>>>>>>> +------------------------------- >>>>>>>> | [email protected] >>>>>>>> | http://malczak.info >>>>>>>> >>>>>>>> >>>>>>>> 2016-10-11 23:42 GMT+02:00 Braeden Profile <[email protected]>: >>>>>>>>> So, just to recap, the proposed solution is to help enums expose >>>>>>>>> associated >>>>>>>>> values via properties, and is not to create enums that are open to >>>>>>>>> extra >>>>>>>>> unnamed cases (RectSize(width:0,height:10))? What I see is that >>>>>>>>> enums would >>>>>>>>> still maintain their standing where an instance is just a selection >>>>>>>>> of a >>>>>>>>> finite number of options, possibly with data attached. In proposal >>>>>>>>> 1, we >>>>>>>>> want some sort of syntax where this… >>>>>>>>> >>>>>>>>> enum RectSize >>>>>>>>> { >>>>>>>>> let height:Int >>>>>>>>> let width:Int >>>>>>>>> case small(width: 30, height: 30) >>>>>>>>> case medium(width: 60, height: 60) >>>>>>>>> case large(width: 120, height: 120) >>>>>>>>> } >>>>>>>>> >>>>>>>>> …is syntactically just like writing this… >>>>>>>>> >>>>>>>>> enum RectSize >>>>>>>>> { >>>>>>>>> case small >>>>>>>>> case medium >>>>>>>>> case large >>>>>>>>> var height:Int >>>>>>>>> { >>>>>>>>> switch self >>>>>>>>> { >>>>>>>>> case .small: return 30 >>>>>>>>> case .medium: return 60 >>>>>>>>> case .large: return 90 >>>>>>>>> } >>>>>>>>> } >>>>>>>>> let width:Int >>>>>>>>> { >>>>>>>>> switch self >>>>>>>>> { >>>>>>>>> case .small: return 30 >>>>>>>>> case .medium: return 60 >>>>>>>>> case .large: return 90 >>>>>>>>> } >>>>>>>>> } >>>>>>>>> } >>>>>>>>> >>>>>>>>> …right? That way, you can write this: >>>>>>>>> >>>>>>>>> var size: RectSize = .small >>>>>>>>> size.height == 30 // true >>>>>>>>> size.rawValue // Error: RectSizes has no property `rawValue`. >>>>>>>>> size.height = 40 // Error: `height` is immutable >>>>>>>>> size = .medium >>>>>>>>> >>>>>>>>> I think we were also (separately) proposing to extend `rawValue` to >>>>>>>>> take all >>>>>>>>> kinds of statically known values, like structs or tuples. Doing that >>>>>>>>> would >>>>>>>>> accomplish much of the same thing. >>>>>>>>> >>>>>>>>> Someone fact-check me here! I really do think something like this >>>>>>>>> would be >>>>>>>>> a good idea, if we could get the right syntax. >>>>>>>>> >>>>>>>>> On Oct 11, 2016, at 7:06 AM, Mateusz Malczak via swift-evolution >>>>>>>>> <[email protected]> wrote: >>>>>>>>> >>>>>>>>> Hi, >>>>>>>>> I think we are here discussing two different aspects of introducing >>>>>>>>> this new feature - code syntax and underlying implementation. >>>>>>>>> In terms of code syntax I would go with first proposal as it seems to >>>>>>>>> me the simplest approach. When it comes to underlying implementation, >>>>>>>>> I can imagine that during compilation internal struct is created, as >>>>>>>>> well as any required property getters. This way you could get a >>>>>>>>> variation of rawValue implementation, at least from theoretical point >>>>>>>>> of view :D >>>>>>>>> >>>>>>>>> -- >>>>>>>>> | Mateusz Malczak >>>>>>>>> +------------------------------- >>>>>>>>> | [email protected] >>>>>>>>> | http://malczak.info >>>>>>>>> >>>>>>>>> >>>>>>>>> 2016-10-10 23:42 GMT+02:00 Haravikk <[email protected]>: >>>>>>>>> >>>>>>>>> >>>>>>>>> On 10 Oct 2016, at 20:34, Mateusz Malczak <[email protected]> >>>>>>>>> wrote: >>>>>>>>> >>>>>>>>> I know, but what I'm saying is that this problem could be solved in >>>>>>>>> the >>>>>>>>> multiple values case by allowing tuples as raw values for enums, >>>>>>>>> since that >>>>>>>>> would allow you to specify both width and height. So it'd look >>>>>>>>> something >>>>>>>>> like this: >>>>>>>>> >>>>>>>>> >>>>>>>>> We have three different possible solution >>>>>>>>> 1. stored properties defined as part of enumeration type >>>>>>>>> enum RectSizes: MyRect >>>>>>>>> { >>>>>>>>> let height:Int >>>>>>>>> let width:Int >>>>>>>>> case Small(width: 30, height: 30) >>>>>>>>> case Medium(width: 60, height: 60) >>>>>>>>> case Large(width: 120, height: 120) >>>>>>>>> } >>>>>>>>> >>>>>>>>> 2. struct as rawValue >>>>>>>>> struct MyRect >>>>>>>>> { >>>>>>>>> var height:Int >>>>>>>>> var width:Int >>>>>>>>> var area:Int {return height:Int*width} >>>>>>>>> } >>>>>>>>> >>>>>>>>> enum RectSizes: MyRect >>>>>>>>> { >>>>>>>>> case Small(30,30) >>>>>>>>> case Medium(60,60) >>>>>>>>> case Large(120,120) >>>>>>>>> } >>>>>>>>> >>>>>>>>> 3. tuples as rawValue >>>>>>>>> enum Format : (width:Int, height:Int) { >>>>>>>>> case small(30, 30) >>>>>>>>> case medium(60, 60) >>>>>>>>> case large(120, 120) >>>>>>>>> >>>>>>>>> var width:Int { return self.rawValue.width } >>>>>>>>> var height:Int { return self.rawValue.height } >>>>>>>>> } >>>>>>>>> >>>>>>>>> Solutions 2 and 3 are quire similar, to get value of a stored property >>>>>>>>> we need to use rawValue or define value getters. In addition in >>>>>>>>> solution 2 we define an additional data type just to be used as an >>>>>>>>> enumeration type rawValue type. In my opinion, first approach would be >>>>>>>>> a best solution, type definition is clear and self-explanatory because >>>>>>>>> it is similar to how enums/classes are defined. >>>>>>>>> >>>>>>>>> >>>>>>>>> -- >>>>>>>>> | Mateusz Malczak >>>>>>>>> >>>>>>>>> >>>>>>>>> Actually I'd say your option 2 here is more similar to option 1 (you >>>>>>>>> seem to >>>>>>>>> be using a struct to define the stored properties instead). The issue >>>>>>>>> here >>>>>>>>> is that storing properties conflicts with what you're actually doing, >>>>>>>>> which >>>>>>>>> is storing case-specific values, which is what rawValue already does, >>>>>>>>> it's >>>>>>>>> just too limited for your current use-case (multiple values). >>>>>>>>> >>>>>>>>> The complete solution would be to introduce the concept of tuples as >>>>>>>>> literals (even though they can't currently conform to types); this >>>>>>>>> would >>>>>>>>> make it a lot easier to support the use of any type as a fixed value >>>>>>>>> for >>>>>>>>> each case (not just tuples). For example, say we introduced as new >>>>>>>>> protocol: >>>>>>>>> >>>>>>>>> protocol ExpressableByTuple { >>>>>>>>> associatedtype TupleType // somehow force this to be a tuple or >>>>>>>>> ExpressableByType type >>>>>>>>> init(tupleLiteral:TupleType) >>>>>>>>> } >>>>>>>>> >>>>>>>>> With a bit of magic all tuples could conform to this protocol with >>>>>>>>> themselves as the literal type, allowing us to use them as enum raw >>>>>>>>> values; >>>>>>>>> likewise this could then be used to more easily enable any custom >>>>>>>>> struct/class for storage in enum cases, as instead of supporting their >>>>>>>>> constructors directly we can just support construction via a tuple >>>>>>>>> literal. >>>>>>>>> >>>>>>>>> >>>>>>>>> My other reason I don't favour option 1, while it looks a bit >>>>>>>>> prettier, is >>>>>>>>> that it's a bit confusing; enums have two types of stored properties, >>>>>>>>> ones >>>>>>>>> that can be changed (and inspected) which is what you get when you >>>>>>>>> declare >>>>>>>>> case small(Int, Int) for example, these are stored as part of the enum >>>>>>>>> itself (so in that example it's 17-bytes on a 64-bit system). However >>>>>>>>> rawValues are more like constants/static values, and don't increase >>>>>>>>> the size >>>>>>>>> of the type, and I just feel that this is the right way to do what >>>>>>>>> you're >>>>>>>>> proposing. >>>>>>>>> >>>>>>>>> _______________________________________________ >>>>>>>>> swift-evolution mailing list >>>>>>>>> [email protected] >>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>>>>> >>>>>>>>> >>>>>>>> _______________________________________________ >>>>>>>> swift-evolution mailing list >>>>>>>> [email protected] >>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>>> >>>>>> _______________________________________________ >>>>>> swift-evolution mailing list >>>>>> [email protected] >>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>> >>>> >>> > _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
