Reputation: 8502
Prior to Swift 2, I would often use enums with associated values and add functions to extract specific values, like so:
public enum Maybe <T> {
case Unknown
case Known(T)
public var value: T? {
switch self {
case .Unknown: return nil
case .Known(let value): return value
}
}
}
This would allow me to do something like this:
let maybe = .Known("Value")
let val = maybe.value ?? "Another Value"
I would like to get rid of these convenience functions and rely on Swift 2's new syntax. This is possible doing something like:
let val: String
if case .Known(let value) = maybe {
val = value
} else {
val = "Another Value"
}
But I can't figure out how to condense this back into a single line using the ??
operator or even ternary operator.
Is this even possible or am I stuck with defining "extraction" optionals on the enum?
The Maybe
enum is just an example, but the solution would need to work on Enums that have multiple associated values... like an Either
:
public enum Either<L, R> {
case Left(Box<L>)
case Right(Box<R>)
public func left() -> L?{
switch self {
case let Left(value):
return value.value
default:
return nil
}
}
public func right() -> R?{
switch self {
case let Right(value):
return value.value
default:
return nil
}
}
}
The syntax I'm looking for would be something like:
let val = (case .Known(let value) = maybe) ?? "AnotherValue"
What I want to do is easily extract an associated value for a specific case, else provide a default.
For Either
it might be something like:
let val = (case .Left(let value) = either) ?? "AnotherValue"
Make sense?
Upvotes: 1
Views: 268
Reputation: 299275
The syntax you want isn't possible in Swift today (and feels unlikely for Swift tomorrow, but I often am surprised). The best available solutions are extraction functions like left() -> L?
and right() -> R?
. case
is not a generic value-returning function that you can extend. See Rob Rix's Either
for some of the best current thinking on this problem.
A key choice in Swift is that there are many statements that are not expressions. switch
is one of them. Until Swift makes things like switch
and if
be expressions, it will be very hard to build this kind of syntax IMO.
Just define ??
for it:
func ??<T>(lhs: Maybe<T>, @autoclosure defaultValue: () throws -> T) rethrows -> T {
switch lhs {
case .Unknown: return try defaultValue()
case .Known(let value): return value
}
}
let maybe = Maybe.Known("Value")
let val = maybe ?? "Another Value"
Doing it this way gives us some nice features in Swift2. For instance, we can lazily evaluate the rhs, and we can handle throwing in the rhs:
func computeIt() -> String {
print("LAZY!")
return "Computed"
}
maybe ?? computeIt() // Does not print "LAZY!"
Maybe.Unknown ?? computeIt() // Does print "LAZY!"
enum Error: ErrorType {
case Failure
}
func fail() throws -> String {
throw Error.Failure
}
try maybe ?? fail() // Doesn't throw
do {
try Maybe.Unknown ?? fail() // throws
} catch {
print("THROW")
}
Upvotes: 3