Consider this code:
enum Type {
case Foo(Int)
case Bar(Int)
var isBar: Bool {
if case .Bar = self {
return true
} else {
return false
}
}
}
That's gross. I would like to write something like this instead:
enum Type {
case Foo(Int)
case Bar(Int)
var isBar: Bool {
return case .Bar = self
}
}
But such a construct does not seem to exist in Swift, or I cannot find it.
Since there's data associated with each case, I don't think it's possible to implement the ~=
operator (or any other helper) in a way that's equivalent to the above expression. And in any case, if case
statements exist for free for all enums, and don't need to be manually implemented.
Thus my question: is there any more concise/declarative/clean/idiomatic way to implement isBar
than what I have above? Or, more directly, is there any way to express if case
statements as Swift expressions?
So, there is a neater way, but requires a 3rd-party package: CasePaths
The idea is they work similarly to KeyPath
s, and they come with a /
operator to trigger it. There is also a ~=
operator to check if a CasePath
matches an instance.
So, you can achieve something like your original example like so:
import CasePaths
enum Type {
case Foo(Int)
case Bar(Int)
var isBar: Bool {
/Self.Bar ~= self
}
}
You can also get the value:
extension Type {
/// Returns the `Int` if this is a `Bar`, otherwise `nil`.
var barValue: Int? {
(/Self.Bar).extract(from: self)
}
}
You can do several other useful things with CasePath
s as well, such as extracting the Foo
values in an array of Type
values:
let values: [Type] = [.Foo(1), .Bar(2), .Foo(3), .Foo(4), .Bar(5)]
let foos = values.compactMap(/Type.Foo) // [1, 3, 4]
let bars = values.compactMap(/Type.Bar) // [2, 5]
I'm sure there is somewhat of a performance cost, but it may not be an issue in your context.
UPDATE 2:
Another workaround... Create a var
that returns an Int
ONLY based on the case, then use a static (or instance, I thought static looked cleaner) method to test equivalence of just the case. It won't clash with Equatable
, you don't have to overload an operator (unless you want to replace the static method with one), and you also wouldn't have to create separate var isFoo
, var isBar
, etc.
I know you used this example to ask a more generic question (how can I use 'if case' as an expression?) but if that's not possible, this may be a valid workaround. I apologize if this treats "the symptoms" not "the problem"
enum Something{
case Foo(Int)
case Bar(Int)
static func sameCase(a: Something, b: Something) -> Bool {
return a.caseValue == b.caseValue
}
var caseValue: Int {
switch self {
case .Foo(_):
return 0
case .Bar(_):
return 1
}
}
//if necessary
var isBar: Bool {
return Something.sameCase(self, b: Something.Bar(0))
}
}
Something.sameCase(.Bar(0), b: .Foo(0)) // false
Something.sameCase(.Bar(1), b: .Foo(2)) // false
Something.sameCase(.Foo(0), b: .Foo(0)) // true
Something.sameCase(.Bar(1), b: .Bar(2)) // true
Something.Bar(0).isBar // true
Something.Bar(5).isBar // true
Something.Foo(5).isBar // false
UPDATE 1:
Ok, so this seems to work. If you overload the == operator to ignore values and return true only when both enums are the same case, you can pass any value in your isFoo method and still determine the type.
I'm assuming you will need to customize this function to accommodate the the associated values, but it seems like a step in the right direction
enum Something {
case Foo(Int)
case Bar(Int)
var isFoo: Bool {
return self == .Foo(0) // number doesn't matter here... see below
}
}
func ==(a: Something, b: Something) -> Bool {
switch (a,b) {
case (.Bar(_), .Bar(_)):
return true
case (.Foo(_), .Foo(_)):
return true
default:
return false
}
}
let oneFoo = Something.Foo(1)
let twoFoo = Something.Foo(2)
let oneBar = Something.Bar(1)
let twoBar = Something.Bar(2)
oneFoo == twoFoo // true
oneFoo == oneFoo // true
oneFoo == oneBar // false
oneFoo == twoBar // false
OLD:
You can use self
and the case
name to directly check which case it is, you don't have to use the case
keyword. Hopefully this will work for your situation:
enum Something{
case Foo(Int)
case Bar(Int)
var isFoo: Bool {
switch self {
case Foo:
return true
case Bar:
return false
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With