Can I use case enum comparison as a boolean expression? - swift

I have an enum with associated values:
enum SessionState {
...
case active(authToken: String)
...
}
I can use case let to compare enum cases with associated values:
if case .active = session.state {
return true
} else {
return false
}
But can I directly return the case as a bool expression? Something like:
// Error: Enum 'case' is not allowed outside of an enum
return (case .active = session.state)
A simple comparison doesn’t work either:
// Binary operator '==' cannot be applied to operands of type 'SessionState' and '_'
return (session.state == .active)

Unfortunately, you cannot (directly) use a case condition as a Bool expression. They are only accessible in statements such as if, guard, while, for etc.
This is detailed in the language grammar, with:
case-condition → case ­pattern­ initializer­
This is then used in a condition:
condition → expression |­ availability-condition |­ case-condition­ | optional-binding-condition
(where expression represents a Bool expression)
This is then used in condition-list:
condition-list → condition­ | condition ­, ­condition-list­
which is then used in statements such as if:
if-statement → if­ condition-list­ code-block­ else-clause­ opt­
So you can see that unfortunately case-condition is not an expression, rather just a special condition you can use in given statements.
To pack it into an expression, you'll either have to use an immediately-evaluated closure:
return { if case .active = session.state { return true }; return false }()
Or otherwise write convenience computed properties on the enum in order to get a Bool in order to check for a given case, as shown in this Q&A.
Both of which are quite unsatisfactory. This has been filed as an improvement request, but nothing has come of it yet (at the time of posting). Hopefully it's something that will be possible in a future version of the language.

No, at this moment.
but may implement in the future.
https://forums.swift.org/t/pitch-case-expressions-for-pattern-matching/20348

Related

Ambiguous use of operator '??'

This article shows: ?? is very time-consuming, and the test found it to be true. So I want to optimize this:
#if DEBUG
public func ?? <T>(left: T?, right: T) -> T {
guard let value = left else {
return right
}
return value
}
#endif
BUT
string = string ?? ""
ERROR: Ambiguous use of operator '??'
The article you link to does not describe how the nil coalescing operator is "time consuming", but states the simple fact that a compound expression of two nil coalescing operator calls as well as other evaluations has a significantly longer build time than if we break down this compound expressions into smaller parts, especially if the former contains lazy evaluations at some point (which is the case e.g. for the evaluation of the rhs of the ?? operator). By breaking down complex expression we help the compiler out; Swift has been known to have some difficulty compiling complex compound statements, so this is expected. This, however, should generally not affect runtime performance, given that you build your implementation for release mode.
By overloading the implementation of the ?? operator during debug with a non-lazy evaluation of the lhs while still using long compound statements shouldn't fully redeem the issue described in the previous clause. If compile time is really an issue for you, use the same approach as the author of the article; break down your complex expressions into several smaller ones, in so doing some of the work of the compiler yourself. Refactoring with the sole purpose of decreasing compile times (which we might believe is the job of the compiler, not us) might be frustrating at times, but is an issue we (currently) has to live with when working with Swift. See e.g. the following excellent article covering the Swift compiler issues with long/complex expressions:
Exponential time complexity in the Swift type checker
W.r.t. to the ambiguity error: for reference, the official implementation of the nil coelescing operator can be found here:
#_transparent
public func ?? <T>(optional: T?, defaultValue: #autoclosure () throws -> T)
rethrows -> T {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
And we may note that it has the same specificity as your own generic implementation (compare the signatures!), meaning the ambiguity error is to be expected. E.g., compare with:
// due to the '#autoclosure', these two have the
// same precedecence/specificity in the overload
// resolution of 'foo(_:)'
func foo<T>(_ obj: T) {}
func foo<T>(_ closure: #autoclosure () -> T) {}
foo("foo") // ERROR at line 9, col 1: ambiguous use of 'foo'
When you implement your ?? overload it as in your answer, typing out Any, this implementation becomes more specific than the generic one, which means it will take precedence in the overload resolution of ??. Using Any is such contexts, however, is generally a bad idea, attempting to mimic dynamic typing rather than relying on Swift's renowned static typing.
if I overloading ??,I can't use <T>...
then
#if DEBUG
public func ?? (left: Any?, right: Any) -> Any {
guard let value = left else {
return right
}
return value
}
#endif
is OK!
BUT I can't get types of return value... 😭

Swift enum evaluation

Using Alamofire we're trying to determine if an error is a certain kind of error (response code 499) as represented by a "nested" AFError enum:
if response.result.isFailure {
if let aferror = error as? AFError {
//THIS LINE FAILS
if (aferror == AFError.responseValidationFailed(reason: AFError.ResponseValidationFailureReason.unacceptableStatusCode(code: 499))) {
....
}
}
}
But this results in the compiler error:
Binary operator '==' cannot be applied to two 'AFError' operands
How can you do this?
Well, you could trying extending AFEError to conform to Equatable in order to use ==, but you are probably better off using switch and pattern matching:
switch aferror {
case .responseValidationFailed(let reason) :
switch reason {
case AFError.ResponseValidationFailureReason.unacceptableStatusCode(let code):
if code == 499 { print("do something here") }
default:
print("You should handle all inner cases")
{
default:
print("Handle other AFError cases")
}
This is the best syntax to ensure (and get the compiler to help you ensure) that all possible error cases and reasons are handled. If you only want to address a single case, like in your example, you can use the newer if case syntax, like this:
if case .responseValidationFailed(let reason) = aferror, case AFError.ResponseValidationFailureReason.unacceptableStatusCode(let code) = reason, code == 499 {
print("Your code for this case here")
}
As I point out here, you cannot, by default, apply the equality (==) operator between cases of an enum that as an associated value (on any of its cases); but there are many other ways to find out whether this is the desired case (and, if there is an associated value, to learn what the associated value may be).

Assign conditional expression in Swift?

Is there a way in Swift to assign conditional expressions similar to this
let foo = if (bar == 2) {
100
} else {
120
}
(or with a switch case).
(Don't want to have to use ternary operator for this).
This kind of assignement is good for functional style / immutability. The expressions have a return value in this case.
Note: it's a general question, this is just simplified example, imagine e.g. a switch case with a lot of values, pattern matching, etc. You can't do that with ternary operator.
Btw also note that there are languages that don't support ternary operator because if else returns a value, so it's not necessary, see e.g. Scala.
You can use a closure to initialize an immutable:
let foo: Int = {
if bar == 2 {
return 100
} else {
return 120
}
}()
The advantage of using a closure is that it's a function, so you can use any complex logic inside, implemented in a clean way and not through nested ternary operators. It can be a switch statement, it can be obtained as the return value of a function followed by some calculations, it can be a pattern matching case, it can be a combination of them all, etc.
Said in other words, it's the same as initializing with the return value of a function, but the difference is that the function is inline and not somewhere else, with readability benefits.
Just for completeness, if the variable is mutable, you can use deferred initialization:
var foo: Int
// Any statement here
if bar == 2 {
foo = 100
} else {
foo = 120
}
// Other statements here
myFunc(foo)
so you can declare a mutable variable, and initialize it anywhere in the same scope, but before using it the variable must be initialized.
Update: Since Swift 2.0, deferred initialization also works with immutables.

Implementing an enum ForwardIndexType

I have been struggling to properly implement the ForwardIndexType protocol for an enum, in particular the handling of the end case (i.e for the last item without a successor). This protocol is not really covered in the Swift Language book.
Here is a simple example
enum ThreeWords : Int, ForwardIndexType {
case one=1, two, three
func successor() ->ThreeWords {
return ThreeWords(rawValue:self.rawValue + 1)!
}
}
The successor() function will return the next enumerator value, except for the last element, where it will fail with an exception, because there is no value after .three
The ForwardTypeProtocol does not allow successor() to return a conditional value, so there seems to be no way of signalling that there is no successor.
Now using this in a for loop to iterate over the closed range of all the possible values of an enum, one runs into a problem for the end case:
for word in ThreeWords.one...ThreeWords.three {
print(" \(word.rawValue)")
}
println()
//Crashes with the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Swift inexplicably calls the successor() function of the end value of the range, before executing the statements in the for loop. If the range is left half open ThreeWords.one..<ThreeWords.three then the code executes correctly, printing 1 2
If I modify the successor function so that it does not try to create a value larger than .three like this
func successor() ->ThreeWords {
if self == .three {
return .three
} else {
return ThreeWords(rawValue:self.rawValue + 1)!
}
}
Then the for loop does not crash, but it also misses the last iteration, printing the same as if the range was half open 1 2
My conclusion is that there is a bug in swift's for loop iteration; it should not call successor() on the end value of a closed range. Secondly, the ForwardIndexType ought to be able to return an optional, to be able to signal that there is no successor for a certain value.
Has anyone had more success with this protocol ?
Indeed, it seems that successor will be called on the last value.
You may wish to file a bug, but to work around this you could simply add a sentinel value to act as a successor.
It seems, ... operator
func ...<Pos : ForwardIndexType>(minimum: Pos, maximum: Pos) -> Range<Pos>
calls maximum.successor(). It constructs Range<T> like
Range(start: minimum, end: maximum.successor())
So, If you want to use enum as Range.Index, you have to define the next of the last value.
enum ThreeWords : Int, ForwardIndexType {
case one=1, two, three
case EXHAUST
func successor() ->ThreeWords {
return ThreeWords(rawValue:self.rawValue + 1) ?? ThreeWords.EXHAUST
}
}
This is an old Question but I would like to sum up some things and post another possible solution.
As #jtbandes and #rintaro already stated a closed range created with the start...end operator is internally created with start..<end.successor()
Afaik this is an intentional behavior in Swift.
In many cases you can also use an Interval where you thought about using a Range or where Swift declared a Range by default. The point here is that intervals aren't collections.
So this is not possible with Intervals
for word in ThreeWords.one...ThreeWords.three {...}
================
For the following I assume the snippet above was just a debug case to cross-check the values.
To declare an Interval you need to explicitly specify the type. Either a HalfOpenInterval (..<) or a ClosedInterval (...)
var interval:ClosedInterval = ThreeWords.one...ThreeWords.four
This requires you to make your enumeration Comparable. Although Int is Comparable already, you still need to add it to the inheritance list
enum ThreeWords : Int, ForwardIndexType, Comparable {
case one=1, two, three, four
func successor() ->ThreeWords {
return ThreeWords(rawValue:self.rawValue + 1)!
}
}
And finally the enumeration need to conform to Comparable. This is a generic approach since your enumeration also conforms to the protocol RawRepresentable
func <<T: RawRepresentable where T.RawValue: Comparable>(lhs: T, rhs: T) -> Bool {
return lhs.rawValue < rhs.rawValue
}
Like I wrote you can't iterate over it in a loop anymore, but you can have a quick cross-check using a switch:
var interval:ClosedInterval = ThreeWords.one...ThreeWords.four
switch(ThreeWords.four) {
case ThreeWords.one...ThreeWords.two:
print("contains one or two")
case let word where interval ~= word:
print("contains: \(word) with raw value: \(word.rawValue)")
default:
print("no case")
}
prints "contains: four with raw value: 4"

swift, optional unwrapping, reversing if condition

Let's say I have function which returns optional. nil if error and value if success:
func foo() -> Bar? { ... }
I can use following code to work with this function:
let fooResultOpt = foo()
if let fooResult = fooResultOpt {
// continue correct operations here
} else {
// handle error
}
However there are few problems with this approach for any non-trivial code:
Error handling performed in the end and it's easy to miss something. It's much better, when error handling code follows function call.
Correct operations code is indented by one level. If we have another function to call, we have to indent one more time.
With C one usually could write something like this:
Bar *fooResult = foo();
if (fooResult == null) {
// handle error and return
}
// continue correct operations here
I found two ways to achieve similar code style with Swift, but I don't like either.
let fooResultOpt = foo()
if fooResult == nil {
// handle error and return
}
// use fooResultOpt! from here
let fooResult = fooResultOpt! // or define another variable
If I'll write "!" everywhere, it just looks bad for my taste. I could introduce another variable, but that doesn't look good either. Ideally I would like to see the following:
if !let fooResult = foo() {
// handle error and return
}
// fooResult has Bar type and can be used in the top level
Did I miss something in the specification or is there some another way to write good looking Swift code?
Your assumptions are correct—there isn't a "negated if-let" syntax in Swift.
I suspect one reason for that might be grammar integrity. Throughout Swift (and commonly in other C-inspired languages), if you have a statement that can bind local symbols (i.e. name new variables and give them values) and that can have a block body (e.g. if, while, for), those bindings are scoped to said block. Letting a block statement bind symbols to its enclosing scope instead would be inconsistent.
It's still a reasonable thing to think about, though — I'd recommend filing a bug and seeing what Apple does about it.
This is what pattern matching is all about, and is the tool meant for this job:
let x: String? = "Yes"
switch x {
case .Some(let value):
println("I have a value: \(value)")
case .None:
println("I'm empty")
}
The if-let form is just a convenience for when you don't need both legs.
If what you are writing is a set of functions performing the same sequence of transformation, such as when processing a result returned by a REST call (check for response not nil, check status, check for app/server error, parse response, etc.), what I would do is create a pipeline that at each steps transforms the input data, and at the end returns either nil or a transformed result of a certain type.
I chose the >>> custom operator, that visually indicates the data flow, but of course feel free to choose your own:
infix operator >>> { associativity left }
func >>> <T, V> (params: T?, next: T -> V?) -> V? {
if let params = params {
return next(params)
}
return nil
}
The operator is a function that receives as input a value of a certain type, and a closure that transforms the value into a value of another type. If the value is not nil, the function invokes the closure, passing the value, and returns its return value. If the value is nil, then the operator returns nil.
An example is probably needed, so let's suppose I have an array of integers, and I want to perform the following operations in sequence:
sum all elements of the array
calculate the power of 2
divide by 5 and return the integer part and the remainder
sum the above 2 numbers together
These are the 4 functions:
func sumArray(array: [Int]?) -> Int? {
if let array = array {
return array.reduce(0, combine: +)
}
return nil
}
func powerOf2(num: Int?) -> Int? {
if let num = num {
return num * num
}
return nil
}
func module5(num: Int?) -> (Int, Int)? {
if let num = num {
return (num / 5, num % 5)
}
return nil
}
func sum(params: (num1: Int, num2: Int)?) -> Int? {
if let params = params {
return params.num1 + params.num2
}
return nil
}
and this is how I would use:
let res: Int? = [1, 2, 3] >>> sumArray >>> powerOf2 >>> module5 >>> sum
The result of this expression is either nil or a value of the type as defined in the last function of the pipeline, which in the above example is an Int.
If you need to do better error handling, you can define an enum like this:
enum Result<T> {
case Value(T)
case Error(MyErrorType)
}
and replace all optionals in the above functions with Result<T>, returning Result.Error() instead of nil.
I've found a way that looks better than alternatives, but it uses language features in unrecommended way.
Example using code from the question:
let fooResult: Bar! = foo();
if fooResult == nil {
// handle error and return
}
// continue correct operations here
fooResult might be used as normal variable and it's not needed to use "?" or "!" suffixes.
Apple documentation says:
Implicitly unwrapped optionals are useful when an optional’s value is confirmed to exist immediately after the optional is first defined and can definitely be assumed to exist at every point thereafter. The primary use of implicitly unwrapped optionals in Swift is during class initialization, as described in Unowned References and Implicitly Unwrapped Optional Properties.
How about the following:
func foo(i:Int) ->Int? {
switch i {
case 0: return 0
case 1: return 1
default: return nil
}
}
var error:Int {
println("Error")
return 99
}
for i in 0...2 {
var bob:Int = foo(i) ?? error
println("\(i) produces \(bob)")
}
Results in the following output:
0 produces 0
1 produces 1
Error
2 produces 99