> 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>
signature.asc
Description: Message signed with OpenPGP using GPGMail
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
