I've recently worked on a ts project, and I really like ts unions!
I think we can extend the current Type Parameter Packs to implement Type Union, we can actually write code that almost works now, but it's not elegant enough:
struct Union<each T: Equatable>: ExpressibleByValue {
// ExpressibleByValue is something we are missing today
let value: (repeat (each T)?)
init(value: repeat (each T)? = nil) { // = nil is missing today
self.value = (repeat each value)
}
static func ~=(pattern: (repeat (each T)?), value: Self) -> Bool {
for (l, r) in repeat (each pattern, each value.value) { // It's not possible today.
guard l == r else { return false }
}
return true
}
}
* Note that adding default values for optional Type Parameter Packs is not a worthwhile exercise since there is a String | String case, but for Union, String | String is meaningless so it can be done.
And the usage is similar to:
let union: String | Int = 1
// `String | Int` is the syntactic sugar for union
// `= 1` is provided by ExpressibleByValue, equivalent to (nil, 1)
switch union {
case let value as String:
print(value)
case let value as Int:
print(value)
}
enum NetworkError {
case deviceNotFound
case wifiNotConnect
}
enum ParserError {
case rootIsArray
case typeIsUndefined
}
let error: NetworkError | ParserError = .typeIsUndefined
switch error {
case .deviceNotFound:
print("deviceNotFound")
case .wifiNotConnect:
print("wifiNotConnect")
case .rootIsArray:
print("rootIsArray")
case .typeIsUndefined:
print("typeIsUndefined")
}
If I remove the ExpressibleByValue and the syntactic sugar, the above code works.
struct Union<each T: Equatable> {
let value: (repeat (each T)?)
init(value: repeat (each T)?) {
self.value = (repeat each value)
}
}
let union: Union<String, Int> = .init(value: nil, 1)
switch union.value {
case let (.some(value), nil):
print(value)
case let (nil, .some(value)):
print(value)
default:
break
}
enum NetworkError {
case deviceNotFound
case wifiNotConnect
}
enum ParserError {
case rootIsArray
case typeIsUndefined
}
let error: Union<NetworkError, ParserError> = .init(value: nil, .typeIsUndefined)
switch error.value {
case let (.some(value), nil):
switch value {
case .deviceNotFound:
print("deviceNotFound")
case .wifiNotConnect:
print("wifiNotConnect")
}
case let (nil, .some(value)):
switch value {
case .rootIsArray:
print("rootIsArray")
case .typeIsUndefined:
print("typeIsUndefined")
}
default:
break
}
Perhaps a better approach would be for the compiler to rewrite union as enum or tuple. If it's tuple, it's perfectly fine with the current compiler.