[swift-evolution] [Pitch] Hashable types on RawRepresentable enums or a protocol for custom enum-like types

Xiaodi Wu xiaodi.wu at gmail.com
Tue Oct 4 10:04:25 CDT 2016


On Tue, Oct 4, 2016 at 2:07 AM, Adrian Zubarev via swift-evolution <
swift-evolution at swift.org> wrote:

> There are still a lot of open questions here to solve.
>
>    - How to statically guarantee the uniqueness + immutability of the
>    hashValues?
>       -
>
>       Should we allow only the usage of an initializer?
>       -
>
>       Sometimes an init might be not enough and you’d wish you can use
>       custom function which would return the same value for the same input you
>       provide.
>
>       struct A : Hashable { … }
>
>       enum B : A {
>           case a: A(someLabel: someInput)
>           case b: createAFunction()
>           case c: createA(with: someOtherInput)
>       }
>
>       -
>
>       Should we allow only value types here or how do we handle reference
>       types?
>
> A protocol to create custom enum-like types would be really interesting
> and useful. Instead of switch checking the value that conforms to that
> new protocol directly I’d suggest that we need some kind of an interface to
> satisfy and enable the enum-like switch usage (or maybe I’m totally wrong
> here).
>
> protocol SomeFancyName : RawRepresentable {
>     var interface: SomeType { get }
> }
>
> struct C : SomeFancyName { /* implement */ }
>
> let c = C.someCase
>
> switch c.interface {
>
> case .someCase:
>     // Handle
>
> case .someOtherCase:
>     // Handle
>
> // No need for `default` when all cases are present
> }
>
> I couldn’t think up a simple and elegant model for such a protocol. Any
> suggestions is welcome.
>
>    -
>
>    Does the core team and the community feel this might have some
>    potential future?
>    -
>
>    Does this impact the ABI somehow?
>
>
A protocol wouldn't work, I don't think, because classes can be extended
and so exhaustiveness can't be guaranteed, and there is no syntax to limit
protocols only to structs and final classes.

While I've sometimes wished RawRepresentable could be used with more
Hashable types, for your motivating problem, you can already get pretty
close to your desired end goal:

```

protocol _RawRepresentable : RawRepresentable { }


extension _RawRepresentable where RawValue : Hashable {

  static func ~= (lhs: Self, rhs: Self) -> Bool {

    return lhs.rawValue == rhs.rawValue

  }

}


struct Example : _RawRepresentable {

  let rawValue: String

  init?(rawValue: String) {

    switch rawValue {

    case "foo", "bar":

      self.rawValue = rawValue

    default:

      return nil

    }

  }

  static let foo = Example(rawValue: "foo")!

  static let bar = Example(rawValue: "bar")!

}


let ex = Example.foo


switch ex {

case Example.foo: // the syntax could be made more intelligent here to
allow `.foo`

  print("Foo!")

case Example.bar:

  print("Bar!")

default:

  fatalError()

}
```

As in the other thread, I don't understand your unwillingness to writing a
default case. It is a very practical solution, and it tells your reader
that your custom type is really meant to be switched over exhaustively. In
fact, it does so at the point of use much more clearly than a protocol
conformance that would not be nearly as obvious to the reader of the code.



> --
> Adrian Zubarev
> Sent with Airmail
>
> Am 4. Oktober 2016 um 08:25:24, Rien (rien at balancingrock.nl) schrieb:
>
> +1.
>
> I have several cases where I cannot use enums, this proposal would solve
> that.
>
>
> > On 03 Oct 2016, at 21:53, Adrian Zubarev via swift-evolution <
> swift-evolution at swift.org> wrote:
> >
> > I made a typo in my previous post.
> >
> > Bikeshdding with correct types:
> >
> > struct A : Hashable { /* implement everything */ }
> >
> > // Variant 1:
> > enum Test : A {
> > case something = A(value: "something")
> > case nothing = A(value: "nothing")
> > }
> >
> > // Variant 2:
> >
> > protocol SomeFancyName : RawRepresentable { … }
> >
> > struct Test : SomeFancyName {
> >
> > let rawValue: A
> > init?(rawValue: A) {
> > // Implement + reject unwanted `A`s
> > }
> >
> > static let something = Test(rawValue: A(value: "something"))
> > static let nothing = Test(rawValue: A(value: "nothing"))
> > }
> >
> > let value = Test.something
> >
> > switch value {
> >
> > case .something:
> > // handle
> >
> > case .nothing:
> > // handle
> >
> > // Because of `SomeFancyName` the switch can use enum-like pattern
> matching + does not need the `default` case when all cases are present
> > }
> >
> >
> >
> >
> > --
> > Adrian Zubarev
> > Sent with Airmail
> >
> > Am 3. Oktober 2016 um 21:50:07, Adrian Zubarev (
> adrian.zubarev at devandartist.com) schrieb:
> >
> >> Hi there,
> >>
> >> I’m interested if this idea has some potential future in Swift or not.
> >>
> >> Currently RawRepresentable enums accept only a subset of literal types
> like String, Character and the Integer family (enum Name : String { … }).
> >>
> >> Sometimes this is not enough for my use-case and I wish I could feed my
> enums with other Hashable types!
> >>
> >> As a workaround I can create a custom struct or even a class and
> conform it to RawRepresentable and fake an enum with some static variables
> (similar to what is done with OptionSet types).
> >>
> >> The problem there is that I cannot use the same switch pattern matching
> like with enums. I’d wish either enums could accept Hashable types (maybe
> with some restriction) or the existence on a protocol to build custom
> enum-like types with strucs/classes and use the same switch pattern
> matching.
> >>
> >> struct A : Hashable { /* implement everything */ }
> >>
> >> // Variant 1:
> >> enum Test : A {
> >> case something = A(rawValue: A(value: "something"))
> >> case nothing = A(rawValue: A(value: "nothing"))
> >> }
> >>
> >> // Variant 2:
> >>
> >> protocol SomeFancyName : RawRepresentable { … }
> >>
> >> struct Test : SomeFancyName {
> >>
> >> let rawValue: A
> >> init?(rawValue: A) {
> >> // Implement + reject unwanted `A`s
> >> }
> >>
> >> static let something = A(rawValue: A(value: "something"))
> >> static let nothing = A(rawValue: A(value: "nothing"))
> >> }
> >>
> >> let value = Test.something
> >>
> >> switch value {
> >>
> >> case .something:
> >> // handle
> >>
> >> case .nothing:
> >> // handle
> >>
> >> // Because of `SomeFancyName` the switch can use enum-like pattern
> matching + does not need the `default` case when all cases are present
> >> }
> >>
> >>
> >>
> >> --
> >> Adrian Zubarev
> >> Sent with Airmail
> >
> > _______________________________________________
> > swift-evolution mailing list
> > swift-evolution at swift.org
> > https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161004/26f1b2de/attachment.html>


More information about the swift-evolution mailing list