[swift-evolution] Ad hoc enums / options

Matthew Johnson matthew at anandabits.com
Wed Jun 1 19:42:30 CDT 2016



Sent from my iPad

> On Jun 1, 2016, at 5:02 PM, Vladimir.S via swift-evolution <swift-evolution at swift.org> wrote:
> 
> > in other words, we could consider allowing this:
> >    func foo(bar: (.fit | .fill)) {
> >      baz(bar: bar)
> >    }
> >    func baz(bar: (.fit | .fill | .florp) { ... }
> >
> > In other words, an ad hoc enum T can be used wherever an ad hoc enum U is
> > expected if T ⊆ U.
> 
> Can't agree with this. Just because the same analogue with tuples : differently defined tuples are different types. Tuples with different order of types in declaration - are different types. So I expect here instance of (.fit | .fill) `bar` is not of the same type as (.fit | .fill | .florp)

They are not the same type but there is a structural subtype relationship between them.  All values of type (.fit | .fill) are also values of type (.fit | .fill | .florp).

> 
> But +1 to be able to 'convert' instance of (.fit | .fill) to instance of (.fit | .fill | .florp). For example(if we'll have init(caseName) and .caseName for enums):
> 
> func foo(bar: (.fit | .fill)) {
>    let bazbar = (.fit | .fill | .florp).init(caseName: bar.caseName)
>    baz(bar: bazbar)
> }
> func baz(bar: (.fit | .fill | .florp) { ... }
> 
> 
> 
>> On 02.06.2016 0:38, Tony Allevato wrote:
>> I find myself agreeing with the idea that ad hoc enums are to enums as
>> structs are to tuples. Based on that analogy, why should an ad hoc enum
>> *need* a name (autogenerated or otherwise) any more than a tuple needs a
>> name? Would those who dislike ad hoc enums argue that this also shouldn't
>> be allowed:
>> 
>>    func foo(bar: (x: Int, y: Int)) {}
>>    let t: (x: Int, y: Int) = (x: 5, y: 5)
>> 
>> If someone writes `(.fit | .fill)` (or whatever the hypothetical syntax
>> might be), that should just *be* the type the same way that `(x: Int, y:
>> Int)` is a type without a name, and that type can be used in argument
>> lists, variables, or whatever. There shouldn't be any worry about
>> declarations across multiple functions colliding or being incompatible any
>> more than we would worry about two functions declaring arguments of type
>> `(x: Int, y: Int)` would collide or be incompatible.
>> 
>> One side of ad hoc enums that I'd like to see explored is that, by being
>> unnamed, they're basically anonymous finite sets and we could apply
>> well-defined subset relationships to them: in other words, we could
>> consider allowing this:
>> 
>>    func foo(bar: (.fit | .fill)) {
>>      baz(bar: bar)
>>    }
>>    func baz(bar: (.fit | .fill | .florp) { ... }
>> 
>> In other words, an ad hoc enum T can be used wherever an ad hoc enum U is
>> expected if T ⊆ U.
>> 
>> 
>> On Wed, Jun 1, 2016 at 1:43 PM L. Mihalkovic via swift-evolution
>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> 
>> 
>>    > On Jun 1, 2016, at 6:51 PM, Vladimir.S <svabox at gmail.com
>>    <mailto:svabox at gmail.com>> wrote:
>>    >
>>    > Yes, I also can support the idea of autogenerated type name (like
>>    Enum_fit_OR_fill) as long as it allows to do all the things we are
>>    discussing here: declare (.fit|.fill) in function, use .fit on calling
>>    side, use (.fit|.fill) to declare temporary variable of type compatible
>>    with such function parameter etc.
>>    >
>> 
>>    It all works because the compiler is just being a thoughless scribe
>>    that just writes the standard enum we don't bother to write ourselves.
>>    Because the heuristic is simple and straightforward then it is
>>    predictible. The enum can be used with its long name be ause it is a
>>    real enum. And writing the short form of it also works because the
>>    compiler knowns uniquely what the long name is everytime it runs into
>>    the short name.
>> 
>> 
>>    > But how do you suggest to define a type of such function in
>>    `typealias` for example? i.e. for func my(option: (.fit|.fill) {..}
>>    >
>>    > typealias MyFunc = ((.fit|.fill)) -> ()
>>    > or as
>>    >
>>    > typealias MyFunc = (Enum_fit_OR_fill) -> ()
>>    >
>> 
>>    Ideally there is no difference whatsoever, there is a single enum, it
>>    is produced at the module level, and it has the long form name.
>> 
>>    There can be rules that would prevent us from doing that with
>>    externally visible APIs, if the core team fuges that we should take the
>>    time to write our enums manually and cleanly to make them visible to
>>    the world, but it is not a necessary rule.
>> 
>> 
>>    >
>>    > But I still can't support the idea of limiting the usage of such
>>    enums - i.e. "To deal with milti site definition, the compiler would
>>    simply flag a error/warning, or be silent in the presence of a new
>>    annotation:". I really think we need then introduce the same rule for
>>    tuples - so no one can use the same tuple declaration in function -
>>    they then should declare separate struct type or use @something for
>>    such functions. Nobody wants such rule for tuples.
>>    >
>> 
>>    Multi site thing is not a limitation... Is is a proposed rule to say
>>    that we are able to be lazy twice without being penalized. Yhe compiler
>>    does not like when we define the same thing twice, and thse short form
>>    amount to doing what he does not let us do. But because this is about
>>    concise and lazy, then the compiler can let us get away with it if we
>>    use an annotation that lets it know that "it is not a mistake.. I
>>    really dont want to write that enum myself, even though I am using the
>>    same abbreviation twice". Otherwise, the compiler would let us know
>>    that the second time could be a mistake because there is already
>>    something with the same name...
>> 
>>    But again this is a separate idea from the core notion of a syntax
>>    sugaring for writing real enums the lazy (clever) way
>> 
>>    >> On 01.06.2016 19:04, L. Mihalkovic wrote:
>>    >> The only problem with this proposal is to consider them ad-hoc
>>    enums... If we view them as having nothing ad-hoc about them and the
>>    thing to be a simple sugaring exercise, then I think all the opositions
>>    on grounds of breaking the language disapear. It still does not mean it
>>    should be done if the core team does not like the idea of encouraging
>>    laziness, or simply do not like what it makes them look like. No matter
>>    what, this type of sugaring exercise has been clearly stated as out of
>>    scope for 3.0
>>    >>
>>    >>>> On Jun 1, 2016, at 2:38 PM, Vladimir.S via swift-evolution
>>    <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>    >>>>
>>    >>>> On 01.06.2016 11:00, Austin Zheng wrote:
>>    >>>> Tuples are a structural type, they are described entirely by the fact
>>    >>>> that they are a tuple, plus their contained types.
>>    >>>>
>>    >>>> Enum cases are not individual types; that precedent exists nowhere in
>>    >>>> Swift. You can't (yet) build a structural type out of something that
>>    >>>> isn't a type. The fact that you had to propose something like
>>    >>>> "AdhocEnumFitFill_2383748" as an autogenerated name for the type
>>    >>>> demonstrates the proposal's weaknesses: a tuple is an ad-hoc type that
>>    >>>> describes itself, while an anonymous enum isn't.
>>    >>>
>>    >>> Yes, I understand the point about the type of such adhoc enum.
>>    >>> The only workaround I can see in this case(if we'd really want to
>>    have it in language) if adhoc enum type will be `(.Fit|.Fill)` i.e.
>>    textual representation if the declared type. As I understand this also
>>    could not be a solution.. I.e. for example
>>    `(Int,String,(.Fit|.Fill))->String`
>>    >>>
>>    >>> From other point of view, adding such type to typesystem will add
>>    some consistence : you can create a function that don't need definition
>>    of separate structure type(tuple will be used) and don't need separate
>>    enum type(ad-hoc enum will be used). I.e. all data the function needs
>>    to process could be described in function definition. Today we need to
>>    use ugly Bool flags in case we want to achieve the same target.
>>    >>>
>>    >>>>
>>    >>>> Now if enum cases were equivalent if they had the same name (like how
>>    >>>> "Int" means the same thing no matter what tuple or generic type it is
>>    >>>> used in), we'd have a good foundation for a self-describing structural
>>    >>>> type. But this isn't how the existing named enum types work. Why would
>>    >>>> it be a good idea to make anonymous enum cases interchangeable by
>>    name?
>>    >>>> Properties on different types aren't interchangeable, even if they
>>    have
>>    >>>> the same type. In fact, no type member that I am aware of is
>>    >>>> interchangeable solely on the basis of name. An
>>    "ArtistAction.Draw" and
>>    >>>> "CowboyAction.Draw" might have the same name, but they mean completely
>>    >>>> different things.
>>    >>>
>>    >>> I don't think they should be 'interchangeable by name', but just
>>    like tuples if you defined adhoc enum with exactly the same cases as
>>    ad-hoc enum in function parameters - then they are of the same type.
>>    >>>
>>    >>> I.e. :
>>    >>>
>>    >>> func foo(option: (.fit|.fill)) {..}
>>    >>>
>>    >>> foo(.fit) // .fit is of type  (.fit|.fill) from definition
>>    >>>
>>    >>> let e : (.fit|.fill) = .fit
>>    >>> foo(e) // e is of (.fit|.fill) type, equal to definition
>>    >>>
>>    >>> but
>>    >>>
>>    >>> func foo2(option: (.fit|.fill|.other)) {..}
>>    >>>
>>    >>> foo2(.fit) // ok, here .fit is of (.fit|.fill|.other) type
>>    >>> foo2(e) --> Error, e is not of type (.fit|.fill|.other)
>>    >>>
>>    >>>>
>>    >>>> Finally, I have to ask: if you are updating your anonymous enum in
>>    >>>> multiple places, how much effort have you actually saved over a
>>    one-line
>>    >>>> enum definition? In fact, tuples are a great example of this: best
>>    >>>> practices usually state that they are good for ad-hoc destructuring,
>>    >>>> such as retrieving multiple return values from a function or pattern
>>    >>>> matching across several values at once, but structs are better
>>    used for
>>    >>>> almost everything else, since they carry semantic meaning that tuples
>>    >>>> don't.
>>    >>>>
>>    >>>
>>    >>> Just the same pros and cons for ad-hoc enums vs enum declaration as
>>    for tuples vs struct declaration. Yes can use it with care and you can
>>    use it in wrong way.
>>    >>>
>>    >>> Btw, I feel like this could be very handy to return adhoc enum:
>>    >>>
>>    >>> func something() -> (.one|.two|.three) {...}
>>    >>>
>>    >>>> I hope that clarifies my thoughts on the matter.
>>    >>>>
>>    >>>> Best, Austin
>>    >>>>
>>    >>>>
>>    >>>>> On Jun 1, 2016, at 12:36 AM, Vladimir.S <svabox at gmail.com
>>    <mailto:svabox at gmail.com>> wrote:
>>    >>>>>
>>    >>>>> On 01.06.2016 9:55, Austin Zheng via swift-evolution wrote:
>>    >>>>>> Maybe it's overkill. My personal opinion is that breaking the
>>    >>>>>> symmetry of the language like this (are there any other types of
>>    >>>>>> function arguments that cannot be passed as either variable values
>>    >>>>>> or literals?) is too much a price to pay. Your library thinks it's
>>    >>>>>> being clever and vends its functions as taking anonymous enum flags,
>>    >>>>>> and now there are a bunch of things I can't do with those functions
>>    >>>>>> anymore.
>>    >>>>>>
>>    >>>>>> A regular enum can be declared in one line anyways:
>>    >>>>>>
>>    >>>>>> enum ScaleCropMode { case Fit, Fill }
>>    >>>>>
>>    >>>>> Why do we have tuples? Struct could be defined by one line `struct
>>    >>>>> SomeValue { var x = 0, y = 0 }` ;-) I.e. from my point of view
>>    >>>>> developer should decide what he/she wants to use: ad-hoc enum or
>>    >>>>> defined enum type *exactly* as now he/she can decide to use the same
>>    >>>>> tuples in multiply functions instead of one defined struct type.
>>    >>>>>
>>    >>>>> I replied regarding the variable on other message. (In short: I think
>>    >>>>> of the same principle as for tuples: you can declare variable `let e:
>>    >>>>> (.fill | .fit) = .fill` and use it)
>>    >>>>>
>>    >>>>>>
>>    >>>>>> Austin
>>    >>>>>>
>>    >>>>>>> On May 31, 2016, at 11:44 PM, Charles Constant
>>    >>>>>>> <charles at charlesism.com <mailto:charles at charlesism.com>
>>    <mailto:charles at charlesism.com <mailto:charles at charlesism.com>>> wrote:
>>    >>>>>>>
>>    >>>>>>>> It breaks the ability to pass in a variable containing the
>>    >>>>>>>> desired
>>    >>>>>>> value, rather than the literal value itself.
>>    >>>>>>>
>>    >>>>>>> Maybe that's appropriate? If the caller is not passing in a
>>    >>>>>>> hardcoded enum case, then that enum is probably general enough
>>    >>>>>>> that it warrants a normal enum. But there are also situations
>>    >>>>>>> where the same function is called from several files in the same
>>    >>>>>>> code-base with different flags. Those are situations where it
>>    >>>>>>> feels like overkill to clutter up my codebase with separate enums,
>>    >>>>>>> only used by a single function.
>>    >>>>>>>
>>    >>>>>>>
>>    >>>>>>>
>>    >>>>>>>
>>    >>>>>>>
>>    >>>>>>> On Tue, May 31, 2016 at 9:24 PM, Austin Zheng via swift-evolution
>>    >>>>>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>    <mailto:swift-evolution at swift.org <mailto:swift-evolution at swift.org>>>
>>    >>>>>>> wrote:
>>    >>>>>>>
>>    >>>>>>> I admire the desire of this proposal to increase the readability
>>    >>>>>>> of code. I'm -1 to the proposal itself, though:
>>    >>>>>>>
>>    >>>>>>> - It breaks the ability to pass in a variable containing the
>>    >>>>>>> desired value, rather than the literal value itself. (Unless you
>>    >>>>>>> actually want a not-so-anonymous enum type whose definition
>>    >>>>>>> happens to live in a function signature rather than somewhere
>>    >>>>>>> you'd usually expect a type definition to live.) - It breaks the
>>    >>>>>>> ability to store a reference to the function in a variable of
>>    >>>>>>> function type (ditto). - Almost every time I've wanted to use one
>>    >>>>>>> of these "anonymous enums" in my code, I've ended up needing to
>>    >>>>>>> use that same enum elsewhere. In my experience, 'lightweight
>>    >>>>>>> enums' don't end up saving much time compared to a full-fledged
>>    >>>>>>> one.
>>    >>>>>>>
>>    >>>>>>> Like Brent said, I have to say no to any proposal that tries to
>>    >>>>>>> make enums synonyms for numerical values. What happens if you
>>    >>>>>>> rearrange your anonymous enum cases between library versions? Do
>>    >>>>>>> you somehow store an opaque case-to-UInt8 table somewhere for
>>    >>>>>>> every anonymous enum you define for resilience? What happens when
>>    >>>>>>> people start bringing back terrible C patterns, like doing
>>    >>>>>>> arithmetic or bitwise ops on the underlying case values? At least
>>    >>>>>>> you have to try pretty hard as it is to abuse Swift's enums.
>>    >>>>>>>
>>    >>>>>>> Austin
>>    >>>>>>>
>>    >>>>>>> On Tue, May 31, 2016 at 8:25 PM, Brent Royal-Gordon via
>>    >>>>>>> swift-evolution <swift-evolution at swift.org
>>    <mailto:swift-evolution at swift.org>
>>    >>>>>>> <mailto:swift-evolution at swift.org
>>    <mailto:swift-evolution at swift.org>>> wrote:
>>    >>>>>>>
>>    >>>>>>>> And the obvious answer is you can have up to 255 of these babies
>>    >>>>>>>> for the anonymous enum type, and be able to pass numerical
>>    >>>>>>>> equivalents UInt8 with compile time substitution. That the
>>    >>>>>>>> ad-hoc enumeration is basically a syntactic shorthand for UInt8,
>>    >>>>>>>> with an enforced upper bound compile time check simplifies
>>    >>>>>>>> everything including switch statements.
>>    >>>>>>>
>>    >>>>>>> If I wanted a language like that, I'd be writing C, not Swift.
>>    >>>>>>>
>>    >>>>>>> -- Brent Royal-Gordon Architechies
>>    >>>>>>>
>>    >>>>>>> _______________________________________________ swift-evolution
>>    >>>>>>> mailing list swift-evolution at swift.org
>>    <mailto:swift-evolution at swift.org>
>>    >>>>>>> <mailto:swift-evolution at swift.org
>>    <mailto:swift-evolution at swift.org>>
>>    >>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>    >>>>>>>
>>    >>>>>>>
>>    >>>>>>>
>>    >>>>>>> _______________________________________________ swift-evolution
>>    >>>>>>> mailing list swift-evolution at swift.org
>>    <mailto:swift-evolution at swift.org>
>>    >>>>>>> <mailto:swift-evolution at swift.org
>>    <mailto:swift-evolution at swift.org>>
>>    >>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>    >>>>>>>
>>    >>>>>>>
>>    >>>>>>
>>    >>>>>>
>>    >>>>>>
>>    >>>>>> _______________________________________________ swift-evolution
>>    >>>>>> mailing list swift-evolution at swift.org
>>    <mailto:swift-evolution at swift.org>
>>    >>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>    >>>>>>
>>    >>>>
>>    >>>>
>>    >>> _______________________________________________
>>    >>> swift-evolution mailing list
>>    >>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>    >>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>    >>
>>    _______________________________________________
>>    swift-evolution mailing list
>>    swift-evolution at swift.org <mailto: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



More information about the swift-evolution mailing list