Which type of value can be compared in switch statement in Swift2 - swift

I wonder which type of value can be compared in switch statement. The official document said:
Cases can match many different patterns, including interval matches, tuples, and casts to a specific type
Is there anything else? Can I compare class type in switch statement?
Suppose I hava a class A:
class A {
}
func == (lhs: A, rhs: A) -> Bool { return true }
Then I can check if two objects of class A are equal. But I still can't do like this:
var a1 = A(); var a2 = A()
switch a1 {
case a2: //do something
}
Although we rarely write codes like these, I'm still very curious about how switch statement works in Swift.

As explained in Expression Patterns,
The expression represented by the expression pattern is compared with the value of an input expression using the Swift standard library ~= operator.
You can define func ~=(lhs: A, rhs: A) if you wish for your custom type to be used in a switch statement.
But I'd also recommend simply using the Equatable protocol, implementing ==, and then you can write if a1 == a2 { ... }.
In fact, the standard library provides
public func ~=<T : Equatable>(a: T, b: T) -> Bool
So if you conform to Equatable, you don't need to provide your own ~=.

Related

Equatable with enum - Swift

How can I validate equal with below source code?
enum ErrorAPI: Equatable, Error {
case CannotFetch(String)
}
func ==(lhs: ErrorAPI, rhs: ErrorAPI) -> Bool {
switch (lhs, rhs) {
case (.CannotFetch(let a), .CannotFetch(let b)) where a == b:
return true
default:
return false
}
}
The enum case CannotFetch has an associated value of type String. That means, when the enum case is set to CannotFetch, a specific String is associated with that case. Read through the official Swift documentation about Enumerations if you need to understand Associated Values.
The func ==() method is a so called Equivalence Operator. You can read more about it in the official Swift documentation about Advanced Operators.
When comparing two enums, lets call them enumOne and enumTwo, this function is implemented to be able to compare those two enums if both cases are CannotFetch(String).
Example:
let enumOne = ErrorAPI.CannotFetch("Hi")
let enumTwo = ErrorAPI.CannotFetch("Hello")
if enumOne != enumTwo {
print("They are not equal!")
} else {
print("They are equal")
}
The line case (.CannotFetch(let a), .CannotFetch(let b)) where a == b: works as follows:
This case is only proceeded if both, enumOne and enumTwo, are set to the case CannotFetch(String)
Take the associated value (=String) of the left hand sided enum, i.e. "Hi" and assign it to the new constant let a.
Take the associated value (=String) of the left hand sided enum, i.e. "Hello" and assign it to the new constant let b.
Additionally, Check if the values behind the constants a and b are equal, i.e. Check if the Strings Hi and Hello are equal.
If all this is true, execute the code block in the case, i.e. return true

Generic Where Clause Ambiguity with Associated Types in Swift

I was writing some example code in a playground and wanted a function that returns the distance between two values, both of which conform to the Strideable protocol in Swift so that I could use the distance(to other: Self) -> Self.Stride function. My implementation was as follows:
func distanceFrom<T: Strideable, U>(_ a: T, to b: T) -> U where T.Stride == U
{
return a.distance(to: b)
}
After observing this function for a while, I realized that I wasn't sure which Stride was being used in the where clause, the one from a or from b. From what I understand it would be possible for a and b to define different associated types for Stride. Also I haven't made any statements to ensure that a.Stride == b.Stride, although I understand that I could expand my where clause to do so.
So, which one would get used to check equivalence to U? To be clear, the question isn't about this particular code block, but rather any situation in which this ambiguity would exist.
a and b are the same type. If you wanted them to be different Strideable types you would add another generic parameter conforming to Strideable such that the function signature appears as follows:
func bar<T: Strideable, V: Strideable, U>(_ a: T, to b: V) -> U where T.Stride == U, V.Stride == U {
return a.distance(to: a) //Trivial return statement (see explanation below)
}
Although the aforementioned code would compile, return a.distance(to: b) would not compile because they (a and b) are different types and the definition of distance in Swift3 is public func distance(to other: Self) -> Self.Stride (note the use of Self which restricts other to the same type as the Strideable upon which this function is called). In conclusion, although you could make a and b different types, for your application it would not make sense to do so.
As further evidence for not being able to call your original posted code with different types please see the attached
Playground screenshot which shows an error when using different types.
However, this works fine in the playground.
func distanceFrom<T: Strideable, U>(_ a: T, to b: T) -> U where T.Stride == U {
return a.distance(to: b)
}
let doubleFoo: Double = 4.5
let intFoo: Double = 4
let g = distanceFrom(doubleFoo, to: intFoo) // gives me a double of -0.5
I hope this helps.

Swift Generic enum function to check case

Is it possible to create a Generic enum function that returns a boolean if an enum instance is an enum Type or does this already exist and I don't know about it?
I use a ton of enums in my projects. Very often I define enums with associated values.
Simple Example:
enum Mode {
case new
case edit(Record) // Record is a struct type
}
I regularly check whether an enum instance is a specific enum case. Many times, however, I don't need to check the associated value. I'm looking for a convenient way to check the case. Each method I know about has a downside.
Method 1-2: If Case pattern matching or Switch case
let myMode = Mode.edit
if case Mode.edit(_) = myMode {
// do something
}
switch mode {
case .edit:
// do something
default:
break
}
Downsides:
Cannot assign the check to a variable directly. Must do so in closure
cannot use this pattern directly as an argument to a function
Method 3-5 Implement an enum function or check value of computed property or Equatable protocol
Downsides:
must be implemented for each enum type
Instead, I'm looking for a way to write a Generic function that returns a boolean if a enum instance matches an enum case. Something that can be written once and be applied to all enums. Similar to Generic function for Struct and Class Types:
func checkType<T, S> (a: T, _: S.Type) -> Bool {
return a is S // though you could just call this directly
}
I don't think there is a good idiomatic way of achieving this. The only thing that comes to mind is to compare the raw memory of your enum instance against a dummy instance with the desired case.
As we don't care about associated values we would only need to require their respective last byte to be identical.
func unsafeEqualityLastByteOnly<A>(_ lhs: A, _ rhs: A) -> Bool {
var (lhs, rhs) = (lhs, rhs)
let offset = MemoryLayout<A>.size - 1
return withUnsafePointer(to: &lhs) { lhsPtr in
withUnsafePointer(to: &rhs) { rhsPtr in
let lhsPtr = unsafeBitCast(lhsPtr, to: UnsafeRawPointer.self)
let rhsPtr = unsafeBitCast(rhsPtr, to: UnsafeRawPointer.self)
return memcmp(lhsPtr.advanced(by: offset), rhsPtr.advanced(by: offset), 1) == 0
}
}
}
This is far from pretty, but it works.
enum Test {
case a(Int)
case b(Int)
}
let a1 = Test.a(1)
let a2 = Test.a(2)
let b1 = Test.b(1)
let b2 = Test.b(2)
unsafeEqualityLastByteOnly(a1, a1) // true
unsafeEqualityLastByteOnly(a1, a2) // true
unsafeEqualityLastByteOnly(a2, a2) // true
unsafeEqualityLastByteOnly(b1, b1) // true
unsafeEqualityLastByteOnly(b1, b2) // true
unsafeEqualityLastByteOnly(b2, b2) // true
unsafeEqualityLastByteOnly(a1, b1) // false
unsafeEqualityLastByteOnly(a1, b2) // false
unsafeEqualityLastByteOnly(a2, b1) // false
unsafeEqualityLastByteOnly(a2, b2) // false
Use your own judgement to decide whether or not this is something you want in your project. It's obviously not a technique that should be recommended without any reservations.

Swift uses the wrong equality function to determine if an array contains an object [duplicate]

I have a custom operator defined globally like so:
func ==(lhs: Item!, rhs: Item!)->Bool {
return lhs?.dateCreated == rhs?.dateCreated
}
And if I execute this code:
let i1 = Item()
let i2 = Item()
let date = Date()
i1.dateCreated = date
i2.dateCreated = date
let areEqual = i1 == i2
areEqual is false. In this case I know for sure that my custom operator is not firing. However, if I add this code into the playground:
//same function
func ==(lhs: Item!, rhs: item!)->Bool {
return lhs?.dateCreated == rhs?.dateCreated
}
//same code
let i1 = Item()
let i2 = Item()
let date = Date()
i1.dateCreated = date
i2.dateCreated = date
let areEqual = i1 == i2
areEqual is true -- I'm assuming my custom operator is fired in this case.
I have no other custom operators defined that would cause a conflict in the non-playground case, and the Item class is the same in both cases, so why is my custom operator not being called outside the playground?
The Item class inherits from the Object class provided by Realm, which eventually inherits from NSObject. I also noticed that if I define non-optional inputs for the overload, when the inputs are optionals it's not fired.
There are two main problems with what you're trying to do here.
1. Overload resolution favours supertypes over optional promotion
You've declared your == overload for Item! parameters rather than Item parameters. By doing so, the type checker is weighing more in favour of statically dispatching to NSObject's overload for ==, as it appears that the type checker favours subclass to superclass conversions over optional promotion (I haven't been able to find a source to confirm this though).
Usually, you shouldn't need to define your own overload to handle optionals. By conforming a given type to Equatable, you'll automatically get an == overload which handles equality checking between optional instances of that type.
A simpler example that demonstrates the favouring of a superclass overload over an optional subclass overload would be:
// custom operator just for testing.
infix operator <===>
class Foo {}
class Bar : Foo {}
func <===>(lhs: Foo, rhs: Foo) {
print("Foo's overload")
}
func <===>(lhs: Bar?, rhs: Bar?) {
print("Bar's overload")
}
let b = Bar()
b <===> b // Foo's overload
If the Bar? overload is changed to Bar – that overload will be called instead.
Therefore you should change your overload to take Item parameters instead. You'll now be able to use that overload to compare two Item instances for equality. However, this won't fully solve your problem due to the next issue.
2. Subclasses can't directly re-implement protocol requirements
Item doesn't directly conform to Equatable. Instead, it inherits from NSObject, which already conforms to Equatable. Its implementation of == just forwards onto isEqual(_:) – which by default compares memory addresses (i.e checks to see if the two instances are the exact same instance).
What this means is that if you overload == for Item, that overload is not able to be dynamically dispatched to. This is because Item doesn't get its own protocol witness table for conformance to Equatable – it instead relies on NSObject's PWT, which will dispatch to its == overload, simply invoking isEqual(_:).
(Protocol witness tables are the mechanism used in order to achieve dynamic dispatch with protocols – see this WWDC talk on them for more info.)
This will therefore prevent your overload from being called in generic contexts, including the aforementioned free == overload for optionals – explaining why it doesn't work when you attempt to compare Item? instances.
This behaviour can be seen in the following example:
class Foo : Equatable {}
class Bar : Foo {}
func ==(lhs: Foo, rhs: Foo) -> Bool { // gets added to Foo's protocol witness table.
print("Foo's overload") // for conformance to Equatable.
return true
}
func ==(lhs: Bar, rhs: Bar) -> Bool { // Bar doesn't have a PWT for conformance to
print("Bar's overload") // Equatable (as Foo already has), so cannot
return true // dynamically dispatch to this overload.
}
func areEqual<T : Equatable>(lhs: T, rhs: T) -> Bool {
return lhs == rhs // dynamically dispatched via the protocol witness table.
}
let b = Bar()
areEqual(lhs: b, rhs: b) // Foo's overload
So, even if you were to change your overload such that it takes an Item input, if == was ever called from a generic context on an Item instance, your overload won't get called. NSObject's overload will.
This behaviour is somewhat non-obvious, and has been filed as a bug – SR-1729. The reasoning behind it, as explained by Jordan Rose is:
[...] The subclass does not get to provide new members to satisfy the conformance. This is important because a protocol can be added to a base class in one module and a subclass created in another module.
Which makes sense, as the module in which the subclass resides would have to be recompiled in order to allow it to satisfy the conformance – which would likely result in problematic behaviour.
It's worth noting however that this limitation is only really problematic with operator requirements, as other protocol requirements can usually be overridden by subclasses. In such cases, the overriding implementations are added to the subclass' vtable, allowing for dynamic dispatch to take place as expected. However, it's currently not possible to achieve this with operators without the use of a helper method (such as isEqual(_:)).
The Solution
The solution therefore is to override NSObject's isEqual(_:) method and hash property rather than overloading == (see this Q&A for how to go about this). This will ensure that your equality implementation will always be called, regardless of the context – as your override will be added to the class' vtable, allowing for dynamic dispatch.
The reasoning behind overriding hash as well as isEqual(_:) is that you need to maintain the promise that if two objects compare equal, their hashes must be the same. All sorts of weirdness can occur otherwise, if an Item is ever hashed.
Obviously, the solution for non-NSObject derived classes would be to define your own isEqual(_:) method, and have subclasses override it (and then just have the == overload chain to it).

How to use Equatable protocol on two objects of an unknown type?

Say I have two variables, and I don't know what type they are (and it's not possible to know what type they are until runtime):
var a: Any
var b: Any
How can I test if they are equal, using the Equatable protocol? I can't just do a == b because that requires that both of the items are the same, Equatable type, and the compiler can't prove that because they could be different types (and one or both might not even be Equatable).
So, is it possible to tell the compiler to check if they both have the same type, and if that type conforms to Equatable, then to use the == operator on them and return the result, otherwise returning false?
If there is no way to do this, is there a good reason Swift prevents this, or is it a current limitation of Swift that could be fixed in the future?
Under the assumption that if a and b differ in type then they are never equal, you can use a generic function with constraint to achieve the goal.
func isEqual<T : Equatable>(a: T, b: T) -> Bool {
return a == b;
}
You cannot have a and b differ in type, as the Equatable protocol assumes that the LHS and RHS of the comparison are of the same type. This seems a reasonable constraint, but one can certainly write a notion of equality that doesn't require this. In these cases, you'll need your own equality protocol.
You use generics and function overload:
func isEqual<T: Equatable>(a: T, b: T) -> Bool {
return a == b
}
func isEqual<T, U>(a: T, b: U) -> Bool {
return false
}
If both variables have the same type, as that type conforms to Equatable, then the compiler will choose the first function, otherwise will go the second one.
This will work for Objective-C objects too, providing you cast to NSObject before calling the function: isEqual(var1 as? NSObject, var2 as? NSObject)