I'm looking to avoid writing unnecessary code. If I define == and < for a type, is it possible to automatically obtain <= (< or ==), > (not < and not ==), or >= (not <)? I would think Swift would provide these operators for free, but that doesn't seem to be the case.
If you make your type conform to Equatable, it will provide a default implementation of != based on your ==.
If you make your type conform to Comparable, it will provide default implementations of >, <=, and >= based on your <.
Note that Comparable extends Equatable, so you must also provide == to conform to Comparable.
Yes. That's exactly how Comparable protocol works. You only have to define 2 operators: < and ==. Swift will figure out the rest.
struct MyStruct : Comparable {
var value: Int
}
func == (lhs: MyStruct, rhs: MyStruct) -> Bool {
return lhs.value == rhs.value
}
func < (lhs: MyStruct, rhs: MyStruct) -> Bool {
return lhs.value < rhs.value
}
let a = MyStruct(value: 1)
let b = MyStruct(value: 42)
print(b > a) // We never defined `>` operator
Yes.
Implement == from Equatable and < from Comparable, and the rest of the operators will use the default implementations as you expect.
To add Comparable conformance to your custom types, define the < and == operators as static methods of your types. The == operator is a requirement of the Equatable protocol, which Comparable extends—see that protocol’s documentation for more information about equality in Swift. Because default implementations of the remainder of the relational operators are provided by the standard library, you’ll be able to use !=, >, <=, and >= with instances of your type without any further code.
Related
I tried the example below from the Swift - Protocols manual page, but I am getting the compiler error:
error: type 'SkillLevel' does not conform to protocol 'Comparable':
enum SkillLevel: Comparable {
case beginner
case intermediate
case expert(stars: Int)
}
var levels = [SkillLevel.intermediate, SkillLevel.beginner,
SkillLevel.expert(stars: 5), SkillLevel.expert(stars: 3)]
for level in levels.sorted() {
print(level)
}
Xcode suggests to add the function below to make it to conform to Comparable:
static func < (lhs: SkillLevel, rhs: SkillLevel) -> Bool {
<#code#>
}
From the manual:
Swift provides a synthesized implementation of Comparable for enumerations that don’t have a raw value. If the enumeration has associated types, they must all conform to the Comparable protocol. To receive a synthesized implementation of <, declare conformance to Comparable in the file that contains the original enumeration declaration, without implementing a < operator yourself. The Comparable protocol’s default implementation of <=, >, and >= provides the remaining comparison operators.
I'm not sure if the enumeration in the code above has a raw value, but if it doesn't, shouldn't the code had compiled without the implementation of the function "<"?
Automatically generating the < operator works if you have something similar to a plain C enum; just the cases and nothing else. But here one of the cases has an associated type, so the compiler doesn't want to guess how you want values ordered.
class Test {
var count: Int;
init(count: Int) {
self.count = count;
}
}
extension Test: Comparable {
static func <(lhs: Test, rhs: Test) -> Bool {
return lhs.count > rhs.count
}
}
When I write this extension everything work okay but when i change < to > compiler error return
Type 'Test' does not conform to protocol 'Equatable'
Comparable extension required write < this function
What is this reason?
If you look at the docs for Comparable, you can see that it inherits from Equatable.
Equatable requires == to be implemented:
static func ==(lhs: Test, rhs: Test) -> Bool {
return lhs.count == rhs.count
}
I should also mention that count does not have an initial value. So you need to add an initializer for Test or a initial value to count.
EDIT:
If you look at the docs for Comparable, you'll find this bit:
Types with Comparable conformance implement the less-than operator (<)
and the equal-to operator (==). These two operations impose a strict
total order on the values of a type, in which exactly one of the
following must be true for any two values a and b:
a == b
a < b
b < a
So you must implement < and == but > is unnecessary. That's why it doesn't work when you only have >.
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)
Say I have a struct where all the memebers already are Equatable.
struct S {
let a : String
let b : Int
let c : Double
}
Would anyone know of any way to automatically make S equatable without manually and boringly defining ==?
In Swift 4.1, Types will now synthesize conformance to Equatable and Hashable if all of the Type's members conform to the protocol.
If all of the type's properties are Equatable, simply define your Struct as Equatable and the required equality methods will be synthesized.
struct S: Equatable {
let a : String
let b : Int
let c : Double
}
Proposal: SE-0185
Status: Implemented (Swift 4.1)
Developers have to write large amounts of boilerplate code to support equatability and hashability of complex types. This proposal offers a way for the compiler to automatically synthesize conformance to Equatable and Hashable to reduce this boilerplate, in a subset of scenarios where generating the correct implementation is known to be possible.
https://github.com/apple/swift-evolution/blob/master/proposals/0185-synthesize-equatable-hashable.md
No, you'll have to define it manually:
func == (l: S, r: S) -> Bool { return l.a == r.a && l.b == r.b && l.c == r.c }
That's about as short as you can make it. But it's pretty darn short (one line!), its meaning is clear, and it's ready for future complications you may add to your type. Be happy!
How can I compare two generic objects. Below is sample code of doing a comparison and this line elem > value throws an error saying Could not find overload for '>' that accepts the supplied arguments
func index<T : Equatable>(array: T[], value: T) -> Int {
for (index, elem) in enumerate(array) {
if elem > value {
return index
}
}
return array.count
}
From the Swift Reference:
The Equatable protocol makes it possible to determine whether two values of the same type are considered to be equal.
There is one required operator overload defined in the protocol: ==.
There is no guarantee that Equatable objects have to implement the > operator, which explains your error.
Take a look at Comparable however. Notice that comparable only needs to overload the < and == operators.
However, if not a < b nor is a == b, you can assume that a > b.
You want Comparable, not Equatable. Equatable only has ==.