[swift-evolution] [Proposal] Enums with stored properties

Xiaodi Wu xiaodi.wu at gmail.com
Wed Oct 12 11:01:08 CDT 2016


On Wed, Oct 12, 2016 at 10:31 AM, Karl <razielim at gmail.com> wrote:

> 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.
> The example was about getting a size (a pair of numbers) based on the case
> of an enum.
> In that case, you need to write a switch statement to return the size. You
> can either do that every time you need a size from the enum, or you can
> extend the enum and make it a computed property.
>
> I do sometimes wonder why we don’t have better accessors. We have the “if
> case let” thing, but that’s horrible. On the rare occasion where it’s
> appropriate, it always takes me 4 or 5 attempts to get the incantation
> correct (single equals!):
>
> if case .anotherThing(let theError) = myValue {
>     print(“oh, no! an error! \(theError)”)
> }
>
> Urf.
>

I entirely agree with everything you just said. And FWIW, this really is
the single most unwieldy incantation in all of Swift--and I think
incantation is the right word here. I'd love it if we could sugar to
something like `if let theError = myValue.anotherThing?.associatedValue` or
something, but of course we could bikeshed this yet again for ages to
little end. Perhaps a topic for the coming spring.


> - Karl
>
> On 12 Oct 2016, at 17:12, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
>
>
> On Wed, Oct 12, 2016 at 10:07 AM, Karl via swift-evolution <swift-
> evolution at swift.org> wrote:
>
>> 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.
>>
>
> 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.
>
>
>> - Karl
>>
>> > On 12 Oct 2016, at 13:52, Rien <Rien at Balancingrock.nl> 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 <razielim at gmail.com> 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 <
>> swift-evolution at swift.org> 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 <Rien at balancingrock.nl>:
>> >>>> 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 <
>> swift-evolution at swift.org> 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
>> >>>>> +-------------------------------
>> >>>>> | mateusz at malczak.info
>> >>>>> | http://malczak.info
>> >>>>>
>> >>>>>
>> >>>>> 2016-10-11 23:42 GMT+02:00 Braeden Profile <jhaezhyr12 at gmail.com>:
>> >>>>>> 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
>> >>>>>> <swift-evolution at swift.org> 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
>> >>>>>> +-------------------------------
>> >>>>>> | mateusz at malczak.info
>> >>>>>> | http://malczak.info
>> >>>>>>
>> >>>>>>
>> >>>>>> 2016-10-10 23:42 GMT+02:00 Haravikk <swift-evolution at haravikk.me>:
>> >>>>>>
>> >>>>>>
>> >>>>>> On 10 Oct 2016, at 20:34, Mateusz Malczak <mateusz at malczak.info>
>> 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
>> >>>>>> swift-evolution at swift.org
>> >>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> >>>>>>
>> >>>>>>
>> >>>>> _______________________________________________
>> >>>>> swift-evolution mailing list
>> >>>>> swift-evolution at swift.org
>> >>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> >>>>
>> >>> _______________________________________________
>> >>> swift-evolution mailing list
>> >>> swift-evolution at swift.org
>> >>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> >>
>> >
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161012/fbcaefb5/attachment.html>


More information about the swift-evolution mailing list