On Jun 30, 2016, at 2:54 PM, Dan Appel via swift-evolution < >> swift-evolution@swift.org> 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\)
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 >>> <swift-evolution@swift.org> 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 >>> (<swift-evolution@swift.org>) 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 <dan.appel00@gmail.com> 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
<Extensible-Enums.md · GitHub;
- Review manager: TBD
<Extensible-Enums.md · GitHub;
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>
<Extensible-Enums.md · GitHub;
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 codebasedo {
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.
<Extensible-Enums.md · GitHub
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 codebasedo {
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 { ... }
}
<Extensible-Enums.md · GitHub
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.
<Extensible-Enums.md · GitHub
on existing code
There is no impact on existing code since this is purely an additive
feature.
<Extensible-Enums.md · GitHub
considered
No alternatives have been considered (yet).
On Thu, Jun 30, 2016 at 1:04 PM David Sweeris via swift-evolution < >>>> swift-evolution@swift.org> 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 < >>>>> swift-evolution@swift.org> 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
> 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
--
Dan Appel
_______________________________________________
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
--