Allowing non-binding pattern matching as a Bool expression?


(Alex Lew) #1

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
     }
}

It gets rid of some boilerplate (explicit return true and return false),
and seems intuitive that it would work given that you can already do

if case .Empty = self {
     return true
} else {
     return false
}

I haven't really thought through all the implications here, so there's
probably something I'm missing. It also may be useful only in such limited
situations that it's not worth the trouble to add.

-Alex


(Andrew Bennett) #2

I've wanted similar too. Although it may be better, instead of Bool, to
have it as an optional tuple, the reason I suggest this is so that you can
get the associated type. For example:

case let .Link(value,link) = self

So your example could be written:

func isEmpty() -> Bool {
    return (case .Empty = self) != nil
}

However I'm not sure about this syntax, perhaps something like this:

func isEmpty() -> Bool {
    return self is case .Empty
}

Alternatively if you want the value you could do:

let (value, link) = self as? case .Link

···

On Tue, Dec 8, 2015 at 3:05 PM, Alex Lew via swift-evolution < swift-evolution@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
     }
}

It gets rid of some boilerplate (explicit return true and return false),
and seems intuitive that it would work given that you can already do

if case .Empty = self {
     return true
} else {
     return false
}

I haven't really thought through all the implications here, so there's
probably something I'm missing. It also may be useful only in such limited
situations that it's not worth the trouble to add.

-Alex

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chris Lattner) #3

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

···

On Dec 7, 2015, at 8:05 PM, Alex Lew via swift-evolution <swift-evolution@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
     }
}


(Kevin Lundberg) #4

Adding additional behavior to is/as that doesn't deal directly with types seems confusing to me. Those operators I feel would be best left for checking and casting types, despite how much nicer this reads.

···

--
Kevin Lundberg

On Dec 8, 2015, at 5:31 AM, Andrew Bennett via swift-evolution <swift-evolution@swift.org> wrote:

I've wanted similar too. Although it may be better, instead of Bool, to have it as an optional tuple, the reason I suggest this is so that you can get the associated type. For example:

case let .Link(value,link) = self

So your example could be written:

func isEmpty() -> Bool {
    return (case .Empty = self) != nil
}

However I'm not sure about this syntax, perhaps something like this:

func isEmpty() -> Bool {
    return self is case .Empty
}

Alternatively if you want the value you could do:

let (value, link) = self as? case .Link

On Tue, Dec 8, 2015 at 3:05 PM, Alex Lew via swift-evolution <swift-evolution@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
     }
}

It gets rid of some boilerplate (explicit return true and return false), and seems intuitive that it would work given that you can already do

if case .Empty = self {
     return true
} else {
     return false
}

I haven't really thought through all the implications here, so there's probably something I'm missing. It also may be useful only in such limited situations that it's not worth the trouble to add.

-Alex

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Alex Lew) #5

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@apple.com> wrote:

> On Dec 7, 2015, at 8:05 PM, Alex Lew via swift-evolution < > swift-evolution@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 :slight_smile:

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


(thorsten@portableinnovations.de) #6

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@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@apple.com> wrote:

> On Dec 7, 2015, at 8:05 PM, Alex Lew via swift-evolution <swift-evolution@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 :slight_smile:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chris Lattner) #7

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.

Happy to help.

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?

MHO, I like #1 with isX/asX - good idea.

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.

I can see that argument, but I think it would both be surprising/weird but would also be less useful. You’d have to do things like:
  if myEnum.asX() != nil {
instead of:
  if myEnum.isX() {

Similarly, the ??, if let, and other mechanics that work with optionals wouldn’t be helpful here, since you’d never want to bind the void value.

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?

No, enum cases should retain their argument lists. It is not related.

-Chris

···

On Dec 8, 2015, at 10:29 PM, Alex Lew <alexl.mail+swift@gmail.com> wrote:


(Alex Lew) #8

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@portableinnovations.de < thorsten@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@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@apple.com> > wrote:

> On Dec 7, 2015, at 8:05 PM, Alex Lew via swift-evolution < >> swift-evolution@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 :slight_smile:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Untracked with Trackbuster <https://trackbuster.com/?sig>


(Andrew Bennett) #9

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@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@portableinnovations.de > <javascript:_e(%7B%7D,'cvml','thorsten@portableinnovations.de');> < > thorsten@portableinnovations.de > <javascript:_e(%7B%7D,'cvml','thorsten@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@swift.org
<javascript:_e(%7B%7D,'cvml','swift-evolution@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@apple.com >> <javascript:_e(%7B%7D,'cvml','clattner@apple.com');>> wrote:

> On Dec 7, 2015, at 8:05 PM, Alex Lew via swift-evolution < >>> swift-evolution@swift.org >>> <javascript:_e(%7B%7D,'cvml','swift-evolution@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 :slight_smile:

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@swift.org
<javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution

Untracked with Trackbuster <https://trackbuster.com/?sig>


(Chris Lattner) #10

I would prefer if no "magic" methods would be generated automatically, but only when marked with @derivestandardmethods (fill in better name here).

Yes, making it opt-in could make sense.

What about enums with more than one associated value?

It would return an optional tuple of the associated values, the tuple corresponds to the case parameter list.

-Chris

···

On Dec 9, 2015, at 8:49 AM, thorsten@portableinnovations.de wrote:

-Thorsten

Am 09.12.2015 um 07:29 schrieb Alex Lew via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@apple.com <mailto:clattner@apple.com>> wrote:

> On Dec 7, 2015, at 8:05 PM, Alex Lew via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(plx) #11

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@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@swift.org <mailto:swift-evolution@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@portableinnovations.de <javascript:_e(%7B%7D,'cvml','thorsten@portableinnovations.de');> <thorsten@portableinnovations.de <javascript:_e(%7B%7D,'cvml','thorsten@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@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@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@apple.com <javascript:_e(%7B%7D,'cvml','clattner@apple.com');>> wrote:

> On Dec 7, 2015, at 8:05 PM, Alex Lew via swift-evolution <swift-evolution@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@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 :slight_smile:

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@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution

Untracked with Trackbuster <https://trackbuster.com/?sig>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Kevin Wooten) #12

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@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@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@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@portableinnovations.de <javascript:_e(%7B%7D,'cvml','thorsten@portableinnovations.de');> <thorsten@portableinnovations.de <javascript:_e(%7B%7D,'cvml','thorsten@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@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@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@apple.com <javascript:_e(%7B%7D,'cvml','clattner@apple.com');>> wrote:

> On Dec 7, 2015, at 8:05 PM, Alex Lew via swift-evolution <swift-evolution@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@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 :slight_smile:

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@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution

Untracked with Trackbuster <https://trackbuster.com/?sig>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(plx) #13

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@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@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@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@portableinnovations.de <javascript:_e(%7B%7D,'cvml','thorsten@portableinnovations.de');> <thorsten@portableinnovations.de <javascript:_e(%7B%7D,'cvml','thorsten@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@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@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@apple.com <javascript:_e(%7B%7D,'cvml','clattner@apple.com');>> wrote:

> On Dec 7, 2015, at 8:05 PM, Alex Lew via swift-evolution <swift-evolution@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@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 :slight_smile:

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@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution

Untracked with Trackbuster <https://trackbuster.com/?sig>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Kevin Wooten) #14

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”)
}

Solvable now via…

switch self {
  case let .Foo(data):
    coder.encodeInteger(…)
    coder.encodeX(..)
  case let .Bar(data):
    coder.encodeInteger(…)
    coder.encodeY(…)
}

It’s only missing an integer raw value assigned by the compiler.

···

On Dec 10, 2015, at 8:42 AM, plx via swift-evolution <swift-evolution@swift.org> wrote:

…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@me.com <mailto:kdubb@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@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@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@portableinnovations.de <javascript:_e(%7B%7D,'cvml','thorsten@portableinnovations.de');> <thorsten@portableinnovations.de <javascript:_e(%7B%7D,'cvml','thorsten@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@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@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@apple.com <javascript:_e(%7B%7D,'cvml','clattner@apple.com');>> wrote:

> On Dec 7, 2015, at 8:05 PM, Alex Lew via swift-evolution <swift-evolution@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@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 :slight_smile:

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@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution

Untracked with Trackbuster <https://trackbuster.com/?sig>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(plx) #15

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”)
}

Solvable now via…

switch self {
  case let .Foo(data):
    coder.encodeInteger(…)
    coder.encodeX(..)
  case let .Bar(data):
    coder.encodeInteger(…)
    coder.encodeY(…)
}

It’s only missing an integer raw value assigned by the compiler.

Well, it’s also missing:

- a compiler-enforced exhaustiveness check here:

guard let enumerationCase = EnumerationCase(rawValue: coder.decodeIntegerForKey(“case”)) else { return nil }
switch enumerationCase {
  case IntegerForFooCase: …
  case IntegerForBazCase: …
  // forgot about Bar! and Quux!
}

…along with compiler-enforced “no typo/no-duplicate/no-forget” checks here:

// in encode:
case let .Foo(data):
  coder.encodeInteger(1, ..)
  coder.encodeX(…)
case let .Bar(data):
  coder.encodeInteger(1, ..) // oopsie
  coder.encodeY(…)
case let .Baz(data):
  coder.encodeInteger(3, ..)
  coder.encodeZ(…)
case let .Quux(data):
  // oopsie! if only i didn’t have to repeat myself!
  coder.encodeW(…)

// in decode:
switch caseInteger {
  case 1: // try to decode Bar // oopsie
  case 2: // try to decode Foo // oopsie
  case 4: // try to decode Baz (//oopsie!)
    // oopsie, forgot about Quux
}

…and once I’m doing this:

extension Example {
  static let SerializationIntegerForFoo: Int = 1
  static let SerializationIntegerForBar: Int = 2
  // etc.

  var caseSerializationInteger: Int {
    get {
      switch self {
        case .Foo(_): return .SerializationIntegerForFoo // etc.
      }
    }
  }
}

…we’re back to the same boilerplate, just written manually and without, e.g., the benefit of exhaustiveness checks in our switch statements.

Outside serialization, the construct has some miscellaneous handiness, e.g.:

enum SomeStateMachineStateData<A,B,C> {
  case Initialized
  case StateA(A)
  case StateB(B)
  case StateC(C)
  case MixedAB(A,B)
}

// ^ *not* obj-c visible

@objc
enum SomeStateMachineState : Int {
  case Initialized
  case StateA
  case StateB
  case StateC
  case MixedAB
}

// ^ objc-visible

…(and similar) which makes it easier to, say, have an obj-c delegate protocol `SomeStateMachineDelegate` that can “follow along” with the associated state machine’s transitions.

Anyways, this isn’t a request that would make previously-unsolvable problems solvable, it’s merely a request for some compiler assistance reducing certain types of boilerplate, offered up in a discussion of other possible enumeration-related boilerplate reduction (isX(), projectX(), etc.).

On an importance scale from 1-10 it’s maybe a 3 for me personally, but I’d hate to see e.g. synthesis of `isX()` make it into the language without having at least seen if anyone else is interested on this one as well.

···

On Dec 10, 2015, at 9:48 AM, Kevin Wooten <kdubb@me.com> wrote:

On Dec 10, 2015, at 8:42 AM, plx via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

…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@me.com <mailto:kdubb@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@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@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@portableinnovations.de <javascript:_e(%7B%7D,'cvml','thorsten@portableinnovations.de');> <thorsten@portableinnovations.de <javascript:_e(%7B%7D,'cvml','thorsten@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@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@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@apple.com <javascript:_e(%7B%7D,'cvml','clattner@apple.com');>> wrote:

> On Dec 7, 2015, at 8:05 PM, Alex Lew via swift-evolution <swift-evolution@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@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 :slight_smile:

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@swift.org <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution

Untracked with Trackbuster <https://trackbuster.com/?sig>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution