Can Comparable protocol be generic? - swift

Consider this struct:
struct Person : Comparable {
let name: String
let age: Int
}
extension Person {
static func < (lhs: Person, rhs: Person) -> Bool {
return lhs.name < rhs.name
}
static func == (lhs: Person, rhs: Person) -> Bool {
return lhs.age == rhs.age && lhs.name == rhs.name
}
}
Person structs now sort by name.
But what if I want to be able to sort by either name or age, is there a way to make the < func generic ?

You cannot make a protocol generic. There are two ways to solve your problem:
You could create a wrapper struct which only contains a Person value, but sorts by a different combination of properties.
Alternatively, you could think of a way to compose methods that compare two things. We've done this in a Swift Talk episode: https://talk.objc.io/episodes/S01E19-from-runtime-programming-to-functions (if you don't want to watch the video, you can read the transcript).

Try this:
struct Person : Comparable {
let name: String
let age: Int
let compareByAge: Bool
}
extension Person {
static func < (lhs: Person, rhs: Person) -> Bool {
if compareByAge {
return lhs.age < rhs.age
}
return lhs.name < rhs.name
}
static func == (lhs: Person, rhs: Person) -> Bool {
if compareByAge {
return lhs.age == rhs.age
}
return lhs.name == rhs.name
}

Related

operator '<' declared in type 'XXX' must be 'static'

I have the following implementation and also added Comparable extension as follows. But I am getting the following error. I wonder how I could able to fix it.
error: operator '<' declared in type 'Item' must be 'static'
func < (lhs: Item, rhs: Item) -> Bool {
^
static
class Item
{
var timeStamp : Date
var value : Int
init(_ value: Int)
{
self.value = value
self.timeStamp = Date()
}
}
extension Item: Comparable
{
func < (lhs: Item, rhs: Item) -> Bool {
return lhs.timeStamp < rhs.timeStamp
}
func == (lhs: Item, rhs: Item) -> Bool {
return lhs.timeStamp == rhs.timeStamp
}
}
The Comparable protocol requires that the < operator implementation is static. It is documented here. You could fix your code by adding the static keyword like so:
static func < (lhs: Item, rhs: Item) -> Bool {
return lhs.timeStamp < rhs.timeStamp
}

Extend Int enum with comparison operators

Very often you have Int enums like this:
enum Difficulty: Int {
case Easy = 0
case Normal
case Hard
}
Difficulty values have a certain meaning and we may want to introduce order for them. For example, somewhere we need to compare:
let isBonusAvailable = level.difficulty.rawVAlue <= Difficulty.Hard.rawValue
I want to make this code a little bit shorter:
let isBonusAvailable = level.difficulty <= .Hard
It can be easily achieved if I add <= directly to the Difficulty. But I wanted to solve this problem in general, so I tried this super-tricky way:
protocol RawRepresentableByInt {
var rawValue: Int { get }
}
extension RawRepresentableByInt {
static func <(lhs: RawRepresentableByInt, rhs: RawRepresentableByInt) -> Bool {
return lhs.rawValue < rhs.rawValue
}
static func >(lhs: RawRepresentableByInt, rhs: RawRepresentableByInt) -> Bool {
return lhs.rawValue > rhs.rawValue
}
static func <=(lhs: RawRepresentableByInt, rhs: RawRepresentableByInt) -> Bool {
return lhs.rawValue <= rhs.rawValue
}
static func >=(lhs: RawRepresentableByInt, rhs: RawRepresentableByInt) -> Bool {
return lhs.rawValue >= rhs.rawValue
}
}
// Error: Extension of protocol 'RawRepresentable' cannot have an inheritance clause
extension RawRepresentable: RawRepresentableByInt where RawValue == Int {
}
It produces a compiler error:
Error: Extension of protocol 'RawRepresentable' cannot have an inheritance clause
I think there is nothing unimplementable in comparison of Int enum in term of logic. Please, help me to trick the Swift compiler. Anyone, who also need such extensions may participate.
It was easier than I thought. So, basically you can use Self instead of creating an additional protocol.
enum Difficulty: Int {
case Easy = 0
case Normal
case Hard
}
extension RawRepresentable where RawValue: Comparable {
static func <(lhs: Self, rhs: Self) -> Bool {
return lhs.rawValue < rhs.rawValue
}
static func >(lhs: Self, rhs: Self) -> Bool {
return lhs.rawValue > rhs.rawValue
}
static func <=(lhs: Self, rhs: Self) -> Bool {
return lhs.rawValue <= rhs.rawValue
}
static func >=(lhs: Self, rhs: Self) -> Bool {
return lhs.rawValue >= rhs.rawValue
}
}
let easy = Difficulty.Easy
print(easy > .Hard) // false

I am trying to create an hasable struct. but every time getting " MyNewHasableStruct does not conform to protocol Hashable "

Following is the code snippet I am having an issue with.
What I understand about Hasbale is that it should comfort '==' and 'hashValue' but due to the error I have override '<' also but still facing the same issue.
struct MyNewHasableStruct {
let oneString: String
let oneInt: Int
}
extension MyNewHasableStruct : Hashable {
var hasValue : Int {
return oneString.hashValue ^ oneInt.hashValue
}
static func == (lhs: MyNewHasableStruct, rhs: MyNewHasableStruct) -> Bool {
return lhs.oneInt == rhs.oneInt && lhs.oneString == rhs.oneString
}
static func < (lhs: MyNewHasableStruct, rhs: MyNewHasableStruct) -> Bool {
return lhs.oneInt < rhs.oneInt && lhs.oneString < rhs.oneString
}
}
var hasValue : Int should be var hashValue : Int
struct MyNewHasableStruct {
let oneString: String
let oneInt: Int
}
extension MyNewHasableStruct : Hashable {
var hashValue : Int {
return oneString.hashValue ^ oneInt.hashValue
}
static func == (lhs: MyNewHasableStruct, rhs: MyNewHasableStruct) -> Bool {
return lhs.oneInt == rhs.oneInt && lhs.oneString == rhs.oneString
}
static func < (lhs: MyNewHasableStruct, rhs: MyNewHasableStruct) -> Bool {
return lhs.oneInt < rhs.oneInt && lhs.oneString < rhs.oneString
}
}

Using a generic protocol type inside of another generic protocol type

I'm trying to figure out how to use a protocol that has a Self or associated type requirement inside of another protocol. Consider the following example:
protocol SortBy {
static var values: [Self] { get }
var title: String { get }
}
protocol Filter {
var sortBy: SortBy { get set }
init(_ filter: Self)
static func == (lhs: Self, rhs: Self) -> Bool
static func != (lhs: Self, rhs: Self) -> Bool
}
I know that since SortBy contains a reference to Self, I can only use it as a generic constraint. What I don't know is if there is any Swift magic I can do to allow what I'm trying to achieve?
Ultimately I want to use the protocols for an example as follows:
enum Sort: SortBy {
case sort1
case sort2
static var values: [Sort] {
return [
.sort1,
.sort2
]
}
var title: String {
switch self {
case .sort1:
return "Sort 1"
case .sort2:
return "Sort 2"
}
}
}
struct FilterExample: Filter {
var sortBy: SortBy
init(_ filter: FilterExample) {
...
}
static func == (lhs: FilterExample, rhs: FilterExample) -> Bool {
...
}
static func != (lhs: FilterExample, rhs: FilterExample) -> Bool {
...
}
}
And in case it matters, I'm using Swift 3.
Thanks for the help in advance.
associatedTypes and generics should be able to get what you want. That said, you should take a look at the Equatable and Comparable protocols. It looks like you're trying to do something related and adoption of those may help you. E.g. adoption of Equatable means you only have to implement == and you get != for free.
protocol SortBy {
static var values: [Self] { get }
var title: String { get }
}
protocol Filter {
associatedtype SortByType: SortBy
var sortBy: SortByType { get set }
init(_ filter: Self)
static func == (lhs: Self, rhs: Self) -> Bool
static func != (lhs: Self, rhs: Self) -> Bool
}
struct FilterExample<T: SortBy>: Filter {
var sortBy: T
init(_ filter: FilterExample<T>) {
self.sortBy = filter.sortBy
}
static func == (lhs: FilterExample, rhs: FilterExample) -> Bool {
return true
}
static func != (lhs: FilterExample, rhs: FilterExample) -> Bool {
return true
}
}

How to properly implement the Equatable protocol in a class hierarchy?

I'm trying to implement the == operator (from Equatable) in a base class and its subclasses in Swift 3. All of the classes will only be used in Swift so I do not want to involve NSObject or the NSCopying protocol.
I started with a base class and a subclass:
class Base {
var x : Int
}
class Subclass : Base {
var y : String
}
Now I wanted to add Equatable and the == operator to Base. Seems simple enough. Copy the == operator signature from the documentation:
class Base : Equatable {
var x : Int
static func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
So far so good. Now for the subclass:
class Subclass : Base {
static override func == (lhs: Base, rhs: Base) -> Bool {
return true
}
}
But this results in an error:
Operator function overrides a 'final' operator function
OK. After some research (still learning Swift 3) I learn that static can be replaced with class to indicate the type method can be overridden.
So I attempt to change static to class in Base:
class Base : Equatable {
var x : Int
class func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
But that results in a new error:
Operator '==' declared in non-final class 'Base' must be 'final'
Ugh. This is far more complicated than it should be.
How do I implement the Equatable protocol and the == operator properly in a base class and a subclass?
After lots of research and some trial and error I finally came up with a working solution. The first step was moving the == operator from inside the class to the global scope. This fixed the errors about static and final.
For the base class this became:
func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
class Base : Equatable {
var x : Int
}
And for the subclass:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
return true
}
class Subclass : Base {
var y : String
}
Now the only part left is figuring out how to call the == operator of the base class from the == operator of the subclass. This led me to the final solution:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
if lhs.y == rhs.y {
if lhs as Base == rhs as Base {
return true
}
}
return false
}
That first if statement results in a call to the == operator in the base class.
The final solution:
Base.swift:
func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
class Base : Equatable {
var x : Int
}
Subclass.swift:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
if lhs.y == rhs.y {
if lhs as Base == rhs as Base {
return true
}
}
return false
}
class Subclass : Base {
var y : String
}
Following the other answers I came up with this:
class Base : Equatable {
var x : Int
static func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
class Subclass : Base {
var y : String
static func == (lhs: Subclass, rhs: Subclass) -> Bool {
return lhs.y == rhs.y && (lhs as Base) == (rhs as Base)
}
}
I know it's been a while since the question is posted, but I hope my answer helps.
TLDR -- Instead of trying to override ==, you provide a custom comparing method, make == call it, and override the custom comparing method if needed.
So you said
All of the classes will only be used in Swift so I do not want to involve NSObject or the NSCopying protocol.
But if you were to subclass NSObject, how will you write your custom comparison method? You will override isEqual(Any?), right? And if you try to conform to Equatable protocol in your subclass, compiler will complain about "Redundant conformance to protocol Equatable" because NSObject already conformed to Equatable.
Now that gives us some hints about how NSObject handles this problem -- it provides a custom comparing method isEqual(Any?), call it inside ==, and its subclasses can override it if needed. You can do the same in your own base class.
Without further ado, let's do some experiments(in Swift 4). Define some classes
class Grandpa: Equatable {
var x = 0
static func ==(lhs: Grandpa, rhs: Grandpa) -> Bool {
return lhs.isEqual(to: rhs)
}
func isEqual(to object: Any?) -> Bool {
guard object != nil && type(of: object!) == Grandpa.self else {
return false
}
let value = object as! Grandpa
return x == value.x
}
}
class Father: Grandpa {
var y = 0
override func isEqual(to object: Any?) -> Bool {
guard object != nil && type(of: object!) == Father.self else {
return false
}
let value = object as! Father
return x == value.x && y == value.y
}
}
class Son: Father {
var z = 0
override func isEqual(to object: Any?) -> Bool {
guard object != nil && type(of: object!) == Son.self else {
return false
}
let value = object as! Son
return x == value.x && y == value.y && z == value.z
}
}
And write some test code
let grandpa1 = Grandpa()
let grandpa2 = Grandpa()
let grandpa3: Grandpa? = nil
let grandpa4: Grandpa? = nil
let father1 = Father()
let father2 = Father()
let father3 = Father()
father3.y = 1
let son1 = Son()
let son2 = Son()
let son3 = Son()
son3.z = 1
print("grandpa1 == grandpa2: \(grandpa1 == grandpa2)")
print("grandpa1 == grandpa3: \(grandpa1 == grandpa3)")
print("grandpa3 == grandpa4: \(grandpa3 == grandpa4)")
print("grandpa1 == father1: \(grandpa1 == father1)")
print("father1 == father2: \(father1 == father2)")
print("father1 == father3: \(father1 == father3)")
print("son1 == son2: \(son1 == son2)")
print("son1 == son3: \(son1 == son3)")
Run it and you should get
grandpa1 == grandpa2: true
grandpa1 == grandpa3: false
grandpa3 == grandpa4: true
grandpa1 == father1: false
father1 == father2: true
father1 == father3: false
son1 == son2: true
son1 == son3: false
Following rmaddy's answer, I came up with a guard approach for testing equality:
Base.swift
static func ==(lhs: Base, rhs: Base) -> Bool {
// ensure class properties match
guard lhs.x == rhs.x else {
return false
}
return true
}
Subclass.swift
static func ==(lhs: Subclass, rhs: Subclass) -> Bool {
// ensure base class properties match
guard lhs as Base == rhs as Base else {
return false
}
// ensure class properties match
guard lhs.y == rhs.y else {
return false
}
return true
}
```