> On Jun 30, 2016, at 2:54 PM, Dan Appel via swift-evolution 
> <[email protected]> wrote:
> 
> Paul,
> 
> That is the current workaround (as the proposal mentions), but it is still 
> missing support for enum features such as associated values and the pattern 
> matching power that they bring.

I don’t believe a developer would be able to extend an enum to support 
arbitrary associated values, the same as the limitation that one cannot extend 
a type today to have extra members. Value types need to have an understood size 
and structure at compile time of the file/module that they are in.

> 
> Dan
> 
> On Thu, Jun 30, 2016 at 1:42 PM Paul Cantrell <[email protected] 
> <mailto:[email protected]>> wrote:
> While it doesn’t give all the “raw value” functionality of enum, it’s 
> possible to use object instance uniqueness to get enum-like behavior that can 
> be extended:
> 
>     public protocol OpenEnum: class, Hashable { }
> 
>     extension OpenEnum {
>       public var hashValue: Int {
>         return ObjectIdentifier(self).hashValue
>       }
>     }
> 
>     public func ==<T: OpenEnum>(lhs: T, rhs: T) -> Bool {
>       return lhs === rhs
>     }
> 
> A library can provide:
> 
>     public final class Color: OpenEnum, CustomStringConvertible {
>       public let description: String
> 
>       public init(description: String) {
>         self.description = description
>       }
> 
>       static let
>         black = Color(description: "black"),
>         white = Color(description: "white")
>     }
> 
> And then in a client project:
> 
>     extension Color {
>       static let
>         puce = Color(description: "puce"),
>         mauve = Color(description: "mauve"),
>         fuchsia = Color(description: "fuchsia")
>     }
> 
> (This is how Siesta provides an extensible set of pipeline stages. 
> https://github.com/bustoutsolutions/siesta/pull/64 
> <https://github.com/bustoutsolutions/siesta/pull/64>)
> 
> With this approach, you still get the .member shortcut in some circumstances:
> 
>     let eyebleedPalette: [Color] = [.fuchsia, .black, .mauve]
> 
> …but not in others:
> 
>     // Compiles
>     switch(color) {
>       case Color.red: print("Danger!")
>       case Color.mauve: print("Dancing!")
>       default: print("Nothing notable")
>     }
> 
>     // Does not compile
>     switch(color) {
>       case .red: print("Danger!")
>       case .mauve: print("Dancing!")
>       default: print("Nothing notable")
>     }
> 
> Given that this already comes close to giving the sort of functionality one 
> would want out of an extensible enum, perhaps it’s better to fill out the 
> gaps in this approach instead of adding a new language feature? This would 
> have the advantage of not adding a keyword, and presumably provide useful 
> behaviors that generalize to patterns other than extensible enums.
> 
> Cheers,
> 
> Paul
> 
>> On Jun 30, 2016, at 3:23 PM, Guillermo Peralta Scura via swift-evolution 
>> <[email protected] <mailto:[email protected]>> wrote:
>> 
>> I think the approach taken by your proporsal is really good. Would love to 
>> have that feature for the language.
>> 
>> El jue., 30 jun. 2016 a las 16:19, Edward Valentini via swift-evolution 
>> (<[email protected] <mailto:[email protected]>>) escribió:
>> 
>> I really like the idea of making it opt in with the extensible keyword as 
>> opposed to opt out with final so this way there is no impact on existing code
>> 
>> On Jun 30, 2016, at 16:15, Dan Appel <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>>> I've had a draft of a proposal lying around for a while which addresses 
>>> exactly this, but I haven't gotten around to sending it out for comments 
>>> yet. Link 
>>> <https://gist.github.com/Danappelxx/41b7c2e86787f75698bd48135cc616f5>.
>>> 
>>> Would appreciate if you guys took a look.
>>> Dan Appel
>>> 
>>> Pasted inline below
>>> 
>>> Extensible Enums
>>> 
>>> Proposal: SE-NNNN 
>>> <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
>>> Author: Dan Appel <https://github.com/danappelxx>
>>> Status: Awaiting review 
>>> <https://gist.github.com/Danappelxx/41b7c2e86787f75698bd48135cc616f5#rationale>
>>> Review manager: TBD
>>>  
>>> <https://gist.github.com/Danappelxx/41b7c2e86787f75698bd48135cc616f5#introduction>Introduction
>>> 
>>> This proposal introduces a new keyword that can be applied to enums which 
>>> allows new cases to be introduced in extensions.
>>> 
>>> Swift-evolution thread: [RFC] Extensible Enums 
>>> <https://lists.swift.org/pipermail/swift-evolution>
>>>  
>>> <https://gist.github.com/Danappelxx/41b7c2e86787f75698bd48135cc616f5#motivation>Motivation
>>> 
>>> Enums are a powerful feature which provides a lot of benefit if you have a 
>>> limited number of behaviors. For example, associated values provide the 
>>> ability to make every case essentially a separate type. However, due to the 
>>> static nature of enums, they cannot be used in situations where they would 
>>> otherwise be a perfect fit.
>>> 
>>> An example of this would be the use of an Error enum like so:
>>> 
>>> enum FileError: ErrorProtocol {
>>>     case fileNotFound(path: String)
>>>     case corruptedFile(bytes: [Int8])
>>> }
>>> func readFile() throws { ... }
>>> 
>>> // elsewhere in the codebase
>>> do {
>>>     try readFile()
>>> } catch let error as FileError {
>>>     switch error {
>>>         case .fileNotFound(let path): // handle error
>>>         case .corruptedFile(let bytes): // handle error
>>>     }
>>> } catch { ... }
>>> While this is generally a good approach, it can be very dangerous for 
>>> library consumers if the author exposes the error to the user. This is due 
>>> to the fact that the switch statement has to be exhaustive and is only 
>>> satisfied when all enum cases have been accounted for. What this means for 
>>> library authors is that every time they add a new case to a public enum, 
>>> they are breaking the exhaustivity of the switch and making their library 
>>> backwards-incompatible.
>>> 
>>> Currently, the best workaround is to use a struct with static instances and 
>>> overloading the ~= operator. This allows for similar switch behavior but 
>>> overall is much less flexible, missing key features such as associated 
>>> values.
>>> 
>>> Another example is when the library is split into multiple modules, where 
>>> the error is defined in the first module and the second module wants to add 
>>> some error cases. An enum is very rarely used in this case because you 
>>> cannot add cases in other modules. Instead, library authors either use an 
>>> error protocol, and add more types that conform to it, or use the struct 
>>> approach shown above. While this is not terrible, adding cases in 
>>> extensions would better translate the intention of the author and adds more 
>>> flexiblity.
>>> 
>>>  
>>> <https://gist.github.com/Danappelxx/41b7c2e86787f75698bd48135cc616f5#proposed-solution>Proposed
>>>  solution
>>> 
>>> The solution proposed is quite simple: add an extensible keyword/modifier 
>>> that can be applied to enums, which would require the default case when 
>>> switched on and allow new cases to be added in extensions.
>>> 
>>> Here is the translation of the very first example to the use an extensible 
>>> enum instead, with a new case added:
>>> 
>>> extensible enum ThingError: ErrorProtocol {
>>>     case fileNotFound(path: String)
>>>     case corruptedFile(bytes: [Int8])
>>>     case failedReadingFile
>>> }
>>> func readFile() throws { ... }
>>> 
>>> // elsewhere in the codebase
>>> do {
>>>     try readFile()
>>> } catch let error as ThingError {
>>>     switch error {
>>>         case .fileNotFound(let path): // handle error
>>>         case .corruptedFile(let bytes): // handle error
>>>         default: // handle future errors that don't exist yet
>>>     }
>>> } catch { ... }
>>> For the second example, we can simply extend the enum in the higher-level 
>>> module.
>>> 
>>> // Module FileProtocol
>>> 
>>> extensible enum FileError: ErrorProtocol {
>>>     case fileNotFound(path: String)
>>> }
>>> 
>>> protocol FileProtocol {
>>>     func read() throws
>>> }
>>> 
>>> // Module File
>>> 
>>> extension FileError {
>>>     case corruptedFile(bytes: [Int8])
>>>     case failedReadingFile
>>> }
>>> 
>>> struct File: FileProtocol {
>>>     func read() throws { ... }
>>> }
>>>  
>>> <https://gist.github.com/Danappelxx/41b7c2e86787f75698bd48135cc616f5#detailed-design>Detailed
>>>  design
>>> 
>>> A new keyword would be added to the language which is only allowed in front 
>>> of the enum keyword. When an enum is marked extensible, new cases can be 
>>> added in extensions and switches that are performed on it require a 
>>> defaultcase.
>>> 
>>>  
>>> <https://gist.github.com/Danappelxx/41b7c2e86787f75698bd48135cc616f5#impact-on-existing-code>Impact
>>>  on existing code
>>> 
>>> There is no impact on existing code since this is purely an additive 
>>> feature.
>>> 
>>>  
>>> <https://gist.github.com/Danappelxx/41b7c2e86787f75698bd48135cc616f5#alternatives-considered>Alternatives
>>>  considered
>>> 
>>> No alternatives have been considered (yet).
>>> 
>>> 
>>> 
>>> 
>>> On Thu, Jun 30, 2016 at 1:04 PM David Sweeris via swift-evolution 
>>> <[email protected] <mailto:[email protected]>> wrote:
>>> By itself, this would break switch statements, since they have to be 
>>> exhaustive.
>>> 
>>> If anyone has any ideas about how to fix that, I'm all ears.
>>> 
>>> - Dave Sweeris
>>> 
>>> > On Jun 30, 2016, at 14:58, Edward Valentini via swift-evolution 
>>> > <[email protected] <mailto:[email protected]>> wrote:
>>> >
>>> >
>>> > I am finding myself in a situation where the most elegant "swifty" 
>>> > solution would be to allow enum extensions to add to existing case 
>>> > options.  For example lets say I'm using a library that has the following 
>>> > enum defined:
>>> >
>>> > enum MyDirection {
>>> >   case east, west
>>> > }
>>> >
>>> > My app for example also makes use of north and south, so I would love to 
>>> > be able to write:
>>> >
>>> > extension MyDirection {
>>> >   case north,south
>>> > }
>>> >
>>> > In objective c, one would probably have defined constants like 
>>> > MyDirectionEast etc...  these would probably have been mapped to ints or 
>>> > strings so a consumer of this library could have easily extended this to 
>>> > add additional functionality, but using constants like that is not very 
>>> > "swifty"
>>> >
>>> > I'm curious what the swift community thinks.
>>> >
>>> > Thank you
>>> > _______________________________________________
>>> > swift-evolution mailing list
>>> > [email protected] <mailto:[email protected]>
>>> > https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> > <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> [email protected] <mailto:[email protected]>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> --
>>> Dan Appel
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 
> --
> Dan Appel
> _______________________________________________
> swift-evolution mailing list
> [email protected] <mailto:[email protected]>
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> <https://lists.swift.org/mailman/listinfo/swift-evolution>

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to