In a bit of back and forth on Twitter about tuples, structs, and enums, Chris wrote up a good overview of why you might use one or the other. Check it out here: Tuples, Structs and Enums. It's a good read.
Here's my dirty secret: I really dislike enums for exactly the reason they exist. The entire purpose of an enum is to solve problems like Chris mentions, "In the previous example, we've used String to use the currency, but what if we want to restrict our program to only currencies we know about?".
I emphasized the problem, well, my problem with enums: a complete lack of extensibility in the case options.
If you build a library that handles currencies and model them with enums, you have forever locked your users into using the currencies you have explicitly allowed in your enum definition.
Take Chris' example:
enum Currency {
case EUR
case USD
case YEN
}
If you, as the consumer of the library, want to add CAD
to the set of supported currencies, you are out of luck.
You can get the source code and make changes, sure. However, then you need to manage two different versions unless you can get that change pushed back. This is not always possible though.
Whenever you make an enum, ask yourself the question: do I need to constrain this a specific set of possibilities (Either
is a good example), or do I need to constrain it to a certain class of possibilities. It might actually be more appropriate to use a interface instead.
An example of that would be the following:
private let usd = USD()
private let eur = EUR()
private let yen = YEN()
protocol CurrencyType {
class var currency: Self { get }
class var symbol: String { get }
}
final class USD : CurrencyType {
class var currency: USD { return usd }
class var symbol: String { return "$" }
}
final class EUR : CurrencyType {
class var currency: EUR { return eur }
class var symbol: String { return "€" }
}
final class YEN : CurrencyType {
class var currency: YEN { return yen }
class var symbol: String { return "¥" }
}
typealias Currency = (value: Int, currency: CurrencyType) // or as a struct
let usd = Currency(5, USD.currency)
There are many other ways to model what I did above; this is just an illustrative example.
I really wish Swift would have solved the problem of case extensions for enums in Swift. It would help in these cases where we really do want a nice set of possible values, but we would also like to have the ability for others to extend those cases in their own code.