Well, I am, and it has an even simpler variant for this use-case (guard case .orc = attack), but it's also limited:
You can't negate this test (guard !(case .orc = attack) doesn't work).
You can't use discriminant expressions (guard case invincibleRace = attack) doesn't work).
You can't compare two cases via patterns (guard case .orc = attack1 == case .orc = attack2 doesn't work).
The mem::discriminant solution deals with the last of these very elegantly, but expressions mixing discriminants and values (both literals and expressions of each) seem to need some pretty intrusive new syntax.
This syntax is why every enum I write winds up with half a screen full of customized properties. It's like a syntax word salad.
What I'd really like is to just get a magical payloadless "family" case for every each case with a payload. Is that any more confusing than we have a second enum, or the impossible-to-remember syntax we're stuck with now?
I really use these things a lot. Maybe I'm doing something wrong, because other Swift coders don't seem to mind as much as I do :)
For these purposes, the negation of guard case is if case; the discriminant solution deals with the last use case as you say. I don't see how the two would ever need to be mixed, since either you are comparing against a statically know case or you're comparing against another instance (there is no third option); if you have a real-world use case I'd be happy to hear it.
guard case syntax is the Swift syntax for pattern matching; the spelling has been debated and, as you say, this is what we've got. We don't create new language features because we don't like the spelling of existing ones.
I thought the OP's invincibleRace property was an example of a non-statically-known case. Do you see that differently, or is its comparison to an instance's case provided by some other construct?
Although I agree that pattern matching is the supported way of accessing the enum discriminant, being able to obtain the value of the discriminant as a Hashable opaque value can also be useful.
OP's invincibleRace property is impossible to express in Swift. OP is proposing a feature to make it possible to make certain comparisons, but OP's design for that feature makes it possible to write invincibleRace, which makes it necessary to be able to compare it.
In today's Swift, either you ask whether a value matches a certain statically known pattern, or you ask whether a value matches another value. It is already possible to do the former, and the latter would be covered by the equivalent of mem::discriminant. There is no third scenario.
I don't have a practical example at the moment, but I imagined a syntax that would let me have it without actually having a value in my contrived example.
It's unclear what the type signature of your imagined initializer would be; I don't think it's expressible in Swift. But that's a minor issue. The point here is that I don't see why you'd ever need it, and hence why we'd ever actually need to worry about designing a syntax for it.
I agree. Until we come up with a compelling use case for obtaining discriminant without an instance, we should not waste time on it. It is usually easy to create an instance with arbitrary associated value just to obtain its discriminant. My example could be better written as:
enum E {
case a(Int)
case b(Double)
case c(String)
}
let someA = E.a(15)
var eCases: [CaseIdentifier<E>: E] = [:]
eCases[CaseIdentifier(someA)] = someA
CaseIdentifier seems like a good idea and fits well with ObjectIdentifier, however I am not sure I would use it. I prefer to add methods to enums and call them rather than use pattern matching. Therefore low priority for me.
Cheers, I get your point. On the other hand, in practice I hope we do anything if the alternative would be, to steal from Orwell, "outright barbaric"
What other feature in Swift meets that description better than...
if case let .foo(bar) = self { return bar } else { return baz }
Is there much gained here by being consistent? Honestly, I'm just taking your word that it is consistent, because I find the syntax too confusing to work out, anyways :)
Yes, you are correct. My 2nd scenario is actually the same as the 3rd scenario:
let attack1 = âŚ
var invincibleRace = MemoryLayout.caseIdentifier(ofValue: attack1)
âŚ
let attack2 = âŚ
guard invincibleRace != MemoryLayout.caseIdentifier(ofValue: attack2) else { ⌠}
The weird part is just that there's no unified syntax. To act conditionally on a literal/statically-known discriminant, you use pattern matching. For a discriminant expression (including a simple variable reference as above), you have to use == equality.
This is a situation which arises in a pattern I tend to use, and that would need CaseIdentifiers:
import UIKit
enum RowInfo {
case title(String)
case value(Int)
}
// This extension could be automatically generated
extension RowInfo {
enum CaseIdentifier {
case title, value
static var allValues: [CaseIdentifier] {
return [.title, .value]
}
}
var `case`: CaseIdentifier {
switch self {
case .title:
return .title
case .value:
return .value
}
}
}
func reuseIdentifier(for rowType: RowInfo.CaseIdentifier) -> String {
switch rowType {
case .title:
return "TitleCell"
case .value:
return "ValueCell"
}
}
func cellClass(for rowType: RowInfo.CaseIdentifier) -> UITableViewCell.Type {
// This should `switch` and use cell subclasses
return UITableViewCell.self
}
func cell(for row: RowInfo, at indexPath: IndexPath, from tableView: UITableView) -> UITableViewCell {
let identifier = reuseIdentifier(for: row.case)
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
return cell
}
func registerCells(to tableView: UITableView) {
for rowType in RowInfo.CaseIdentifier.allValues {
let `class` = cellClass(for: rowType)
let identifier = reuseIdentifier(for: rowType)
tableView.register(`class`, forCellReuseIdentifier: identifier)
}
}
The issue arises mainly from the fact that the type I'm using (RowInfo) describes fully-defined row content, while some functionality (registerCells(to:)) only cares about the existing cases.
Did I miss some alternative way of doing it which would make CaseIdentifier unnecessary?
Parsers are my compelling case. I tend to create a parser per case to accept that case and then I combine them. A discriminant helps me be sure that I represent each case. I don't want or need an associated value.