0xsoria
(Gabriel Sória)
1
Hello!
Have you ever considered the convenience of integrating the if case syntax with && or || operators?
This enhancement could prove particularly handy for enums with multiple cases, especially in scenarios where employing a switch statement feels excessive.
For instance:
enum MyEnum {
case first(data: String)
case second(data: String)
case third(data: [String])
case fourth
case fifth
case sixth
}
let first = MyEnum.first(data: "Gabriel")
let second = MyEnum.second(data: "Aurora")
var third = MyEnum.third(data: ["Gabriel", "Aurora", "Marcela"])
if case .first(let data) = first || case .second(let data) = second {
print(data)
}
In this envisioned implementation, I imagine that utilizing || would require all cases to possess parameters of the same type (as demonstrated above) or no parameters at all. Conversely, with &&, compatibility would extend to any combination of parameters.
Consider:
if case .first(let firstData) = first && case .second(let secondData) = second && case .third(let thirdData) = third {
print(firstData)
print(secondData)
print(thirdData)
}
What do you think?
bbrk24
2
This can already be done, it's just spelled with , rather than &&.
tera
3
The || though is not easy. For the type (I changed your type a bit to add more use cases):
enum MyEnum {
case first(string: String)
case second(string: String)
case third(strings: [String], someInt: Int)
case fourth(Int, Double)
case fifth
}
My long standing wish is to have an autogenerated code:
extension MyEnum: SomeMagicProtocol {
// Autogenerated conformances
var first: String! {
get {
if case .first(let string) = self { string } else { nil }
}
set {
self = .first(string: newValue)
}
}
var second: String! {
get {
if case .second(let string) = self { string } else { nil }
}
set {
self = .second(string: newValue)
}
}
var third: (strings: [String], someInt: Int)! {
get {
if case let .third(strings, someInt) = self { (strings: strings, someInt: someInt) } else { nil }
}
set {
self = .third(strings: newValue.strings, someInt: newValue.someInt)
}
}
var fourth: (Int, Double)! {
get {
if case let .fourth(x, y) = self { (x, y) } else { nil }
}
set {
self = .fourth(newValue.0, newValue.1)
}
}
var fifth: MyEnum! {
get {
if case .fifth = self { .fifth } else { nil }
}
set {
self = .fifth
}
}
}
which could happen when my type marked with SomeMagicProtocol.
protocol SomeMagicProtocol {}
Here's how your example could look like then:
let a = MyEnum.first(string: "Gabriel")
let b = MyEnum.second(string: "Aurora")
if let string = a.first ?? b.second {
print(string)
}
if let strings = a.third?.strings {
print(strings)
}
xwu
(Xiaodi Wu)
4
It's not difficult. As the case spelling would hint, you switch over the two values:
switch (first, second) {
case (.first(let data), _):
fallthrough
case (_, .second(let data)):
print(data)
default:
break
}
1 Like
Jon_Shier
(Jon Shier)
5
Much of this can be accomplished using macros, such as PointFree’s swift-case-paths, to generate the Bool accessors for each case. I still think it would be better for this to be built it, and for the case syntax to be better integrated, but there are workarounds now.
mbrandonw
(Brandon Williams)
6
Yeah, for what it's worth, using our case paths library your first situation can be expressed like so:
import CasePaths
@CasePathable
@dynamicMemberLookup
enum MyEnum {
case first(data: String)
case second(data: String)
case third(data: [String])
case fourth
case fifth
case sixth
}
let value1 = MyEnum.first(data: "Gabriel")
let value2 = MyEnum.second(data: "Aurora")
var value3 = MyEnum.third(data: ["Gabriel", "Aurora", "Marcela"])
if let data = value1.first ?? value2.second {
print(data)
}
And your second situation like this:
if
let data1 = value1.first,
let data2 = value2.second,
let data3 = value3.third
{
print(data1)
print(data2)
print(data3)
}
And there are a few other niceties too, such as an expression version of case pattern matching:
if value1.is(\.first) {
// ...
}
Of course, this is only viable if you are OK depending on a 3rd party library and OK with the cost of macros.
6 Likes