[swift-evolution] Allowing non-binding pattern matching as a Bool expression?

plx plxswift at icloud.com
Thu Dec 10 09:42:36 CST 2015


The motivating/most-common case for me is serialization:

coder.encodeInteger(self.enumerationCase.rawValue, forKey: “case")
switch self {
  case let .Foo(data): coder.encodeX(data, forKey: “data”)
  case let .Bar(data): coder.encodeY(data, forKey: “data”)
}

…etc., and the obvious use of the above when decoding (decode the case, switch on it, decode data).

Once you have it it’s occasionally handy in other contexts, but that’s by far the main motivation.

> On Dec 10, 2015, at 9:35 AM, Kevin Wooten <kdubb at me.com> wrote:
> 
> I get the gist of what you are requesting and I can see it might me useful but given that
> 
> if case .Foo(_) == ex {
> }
> 
> Provides comparison, regardless of the # of associated values in .Foo, I’m not sure generating a second enum just to carry a diminished amount of information is worth it.
> 
> 
>> On Dec 10, 2015, at 8:24 AM, plx via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> FWIW, as long as we’re asking for some compiler assistance generating useful enumeration-related boilerplate, I’d like to request that something along these lines be possible in some version of Swift:
>> 
>> @synthesize_case_enum
>> enum Example {
>> 
>>   case Foo(X)
>>   case Bar
>>   case Baz(Y)
>> 
>> }
>> 
>> …which would then by default expand to something like this:
>> 
>> enum ExampleCase : Int {
>>   
>>   case Foo
>>   case Bar
>>   case Baz
>> 
>> }
>> 
>> extension Example {
>>  
>>   var enumerationCase: ExampleCase {
>>     get {
>>       switch self {
>>         case .Foo(_): return .Foo
>>         case .Bar(_): return .Bar
>>         case .Baz(_): return .Baz
>>       }
>>     }
>>   }  
>> 
>> }
>> 
>> …in the default case, but with some customization of the synthesized enumeration-case-enum’s details.
>> 
>> For the remaining proposals:
>> 
>> - `isX()` is nice (and should always `-> Bool`, IMHO)
>> - `projectX()` is nice and should:
>>   - *not* be defined for enums w/out associated values
>>   - `-> T?` for enums w/ a single associated value
>>   - -> the full tuple, for enums w/ multiple associated values, e.g.:
>>     - `Foo(A,B)` yields `projectFoo() -> (A,B)?
>>     - `Event(place: Place, time: Time)` -> (place: Place, time: Time)?
>>     - (which is essentially the same as for the single-value case, just making it explicit)
>> 
>> …which has the benefit of predictability (and if you need more convenience, replace your tuples with actual classes-or-structs). 
>> 
>> Finally, as convenient as the `associatedValue` proposal is down below, it seems better-served by some sort of generalized delegation construct; at a minimum, I don’t think it's that uncommon to have N cases all wrapping the same underlying type, and in such cases working with an `Any?` would feel rather clunky.
>> 
>>> On Dec 9, 2015, at 3:00 PM, Andrew Bennett via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> I really like this solution and perhaps you can also go @projectable("empyValue") if the default name is a bad choice. See @obc for similar usage.
>>> 
>>> On Thursday, 10 December 2015, Alex Lew via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> Semantically, I believe enums with more than one associated value are actually just enums with one associated value, of tuple type.
>>> 
>>> I'm not convinced it would be bad to do magic function generation, but it's worth considering explicit syntax. If we were to use @ compile-time attributes, maybe it could be a word before the case -- like @projectable or @selectable (or some better name)?
>>> 
>>> enum List<T> {
>>>      @projectable case Empty
>>>      indirect case FirstAndRest(T, List<T>)
>>> }
>>> 
>>> generates:
>>> myList.isEmpty() -> Bool
>>> 
>>> Another option: some sort of @reflectable attribute on the enum itself.
>>> 
>>> @reflectable enum Pet {
>>>      case Cat(name: String, color: String)
>>>      case Dog(name: String, breed: String)
>>>      case Bear(name: String, isHibernating: Bool)
>>> }
>>> 
>>> And one other option, in a very different direction, that seems weird but maybe has its benefits:
>>> 
>>> What if we exposed an associatedValue computed property of type Any? (It would be unit type for cases with no associated value.)
>>> 
>>> You could then do something like this:
>>> 
>>> enum Contact {
>>>      case Person(String) // name
>>>      case Business(String) // org name
>>>      case FamilyMember(String) // relation name
>>>      case RecentCall(Int) // a contact from caller id, only store the phone number
>>> 
>>>      func name() -> String {
>>>           return associatedValue as? String ?? "Unknown (\(associatedValue as! Int))"
>>>      }
>>> }
>>> 
>>> Essentially, it allows you to project out a relatively common associated value (in this case a string) without much boilerplate. It's also just one thing for the compiler to generate, instead of n. Not crazy about any of these... just brainstorming. It may also be that a concise switch-like control flow expression eliminates the need for this.
>>> 
>>> -Alex
>>> 
>>> On Wed, Dec 9, 2015 at 11:49 AM, thorsten at portableinnovations.de <javascript:_e(%7B%7D,'cvml','thorsten at portableinnovations.de');> <thorsten at portableinnovations.de <javascript:_e(%7B%7D,'cvml','thorsten at portableinnovations.de');>> wrote:
>>> I would prefer if no "magic" methods would be generated automatically, but only when marked with @derivestandardmethods (fill in better name here).
>>> 
>>> As to naming I like the proposal #1 by Alex.
>>> 
>>> What about enums with more than one associated value?
>>> 
>>> -Thorsten
>>> 
>>> Am 09.12.2015 um 07:29 schrieb Alex Lew via swift-evolution < swift-evolution at swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution at swift.org');>>: 
>>> 
>>>> Thanks, Chris, for all the time you're putting into responding to these proposals (and the kindness with which you're responding!). I really like that solution.
>>>> 
>>>> Brainstorming some names for the auto-generated functions:
>>>> 
>>>> 1. If a case has no associated values, isX() -> Bool is generated, where X is the case name.
>>>>     If a case has an associated value of type T, asX() -> T? is generated, where X is the case name.
>>>>     This mirrors is/as? operators, which return Bool / Optional respectively.
>>>> 2. projectX() -> Bool / projectX() -> T?
>>>> 3. isX() -> Bool / xValue() -> T?
>>>> 
>>>> Another option (probably the wrong option, but it's worth putting out there) is that instead of returning Bool in the no-associated-value case, we return ()?. This would make me feel better about using the same naming convention (asX(), projectX(), xValue(), etc.) for each case, and would allow for != nil checks on all cases. But it would probably be a little confusing for newcomers to the language.
>>>> 
>>>> One (potentially misguided) question. I noticed in proposal 0002 (on removing function currying) that there are "plans to move away from the arguments-are-a-single-tuple model" in the near future. Would this also affect associated values of enums? That is, might
>>>> 
>>>> case Dog(name: String, age: Int, breed: String)
>>>> 
>>>> one day not have the semantics of a single associated value of type (name: String, age: Int, breed: String)? Or is the de-ML-ification planned only for function arguments?
>>>> 
>>>> -Alex
>>>> 
>>>> On Wed, Dec 9, 2015 at 12:45 AM, Chris Lattner <clattner at apple.com <javascript:_e(%7B%7D,'cvml','clattner at apple.com');>> wrote: 
>>>> 
>>>> > On Dec 7, 2015, at 8:05 PM, Alex Lew via swift-evolution <swift-evolution at swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution at swift.org');>> wrote:
>>>> >
>>>> > Hi all,
>>>> >
>>>> > Curious to hear thoughts on allowing non-binding pattern matches to be used as boolean values outside of an if, guard, for...in, while, switch, etc. Something like:
>>>> >
>>>> > enum List<T> {
>>>> >      case Empty
>>>> >      indirect case Link(T, List<T>)
>>>> >
>>>> >      func isEmpty() -> Bool {
>>>> >           return case .Empty = self
>>>> >      }
>>>> > }
>>>> 
>>>> I agree with you that this is a problem that we should solve, but I think it could be solved in a different way.  Imagine if: 
>>>> 
>>>> enum Foo { 
>>>>   case X(a : Float), Y, Z(a : Int) 
>>>> } 
>>>> 
>>>> automatically synthesized these members (the exact names are just a strawman proposal, not serious :-) 
>>>> 
>>>> extension Foo { 
>>>>   func isX() -> Float? {…} 
>>>>   func isY() -> Bool {…} 
>>>>   func isZ() -> Int? {…} 
>>>> } 
>>>> 
>>>> This would tie into all of the mechanics we have for dealing with optionals, e.g. if/let and ?? 
>>>> 
>>>> -Chris
>>>> 
>>> 
>>>> _______________________________________________ 
>>>> swift-evolution mailing list 
>>>> swift-evolution at swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution at swift.org');> 
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution> 
>>> Untracked with Trackbuster <https://trackbuster.com/?sig>
>>> 
>>>  _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution <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
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151210/3c9a7be8/attachment.html>


More information about the swift-evolution mailing list