Comparing two metaclasses (AnyClass) - swift

Is there any way to implement these custom comparison functions in the current state of Swift?
/// Tests if `lhs` is the same class as (or a subclass of) `rhs`.
public func == (lhs: AnyClass, rhs: AnyClass) -> Bool {
fatalError("unimplemented")
}
/// Tests if `lhs` is the same class as `rhs`.
public func === (lhs: AnyClass, rhs: AnyClass) -> Bool {
fatalError("unimplemented")
}
I would use them like so:
class A {}
class B : A {}
A.self == A.self // true
B.self == A.self // true
A.self == B.self // false
A.self === A.self // true
B.self === A.self // false
A.self === B.self // false
Any ideas?

Related

Swift subclass conform to equatable called super class implemenation?

class Base: Equatable {
static func == (lhs: Base, rhs: Base) -> Bool {
lhs.id == rhs.id
}
let id: String
init(id: String) {
self.id = id
}
}
class SubClass: Base {
public var id2: String?
public init(id1: String, id2: String? = nil) {
self.id2 = id2
super.init(id: id1)
}
static func == (lhs: SubClass, rhs: SubClass) -> Bool {
lhs.id2 == rhs.id2 && lhs.id == rhs.id
}
}
print(a != b) // result: false
// Calls `Base` class's static func ==
print(a == b) // result: false
// Calls `SubClass` class's static func ==
I have a simple super class and subclass, subclass inherits Base and also implements
static func ==
When calling a != b, it calls Base class's == implementation instead of SubClass's == implementation, why?
But when calling a == b, it actually call's SubClass's == implementation, why?
I expect both != and == calls SubClass's == implementation
This problem existed long time ago since the improvement of Swift language (https://github.com/apple/swift-evolution/blob/master/proposals/0091-improving-operators-in-protocols.md)
And the author also mentioned about this problem. You can check that. https://github.com/apple/swift-evolution/blob/master/proposals/0091-improving-operators-in-protocols.md#class-types-and-inheritance
One of the solution I found is define an isEqual function.
class Superclass: Equatable {
var foo: Int
init(foo: Int) {
self.foo = foo
}
func isEqual(to instance: Superclass) -> Bool {
return self.foo == instance.foo
}
static func ==(lhs: Superclass, rhs: Superclass) -> Bool {
return type(of: lhs) == type(of: rhs) && lhs.isEqual(to: rhs)
}
}
class Subclass: Superclass {
var bar: String
init(foo: Int, bar: String) {
self.bar = bar
super.init(foo: foo)
}
override func isEqual(to instance: Superclass) -> Bool {
guard let instance = instance as? Subclass else {
return false
}
return self.foo == instance.foo && self.bar == instance.bar
}
}
let a = Subclass(foo: 1, bar: "a")
let b = Subclass(foo: 1, bar: "b")
print(a == b) // False
print(a != b) // True
Reference:
https://forums.swift.org/t/method-dispatching-issue-with-subclasses-implementing-equatable-protocol/4925
https://forums.swift.org/t/implement-equatable-protocol-in-a-class-hierarchy/13844

Overload Equality Operator (==) to compare String and Int

I am experiencing with protocols and challenged myself to write a code snippet that overloads the == operator so that it returns true when I compare a random String with value "42" with a Int of value 42. Please don't question the usefulness by simply returning 42 on a String, the main point is getting the Equality Operator to run on the two different types.
Here is what I tried:
Version 1
import Foundation
protocol IntTransformable: Equatable {
func toInt() -> Int
}
extension String: IntTransformable {
func toInt() -> Int {
return 42
}
}
extension Int: IntTransformable {
func toInt() -> Int {
return self
}
}
extension IntTransformable {
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.toInt() == rhs.toInt()
}
}
// throws: Ambiguous reference to operator function '=='
if "42" == 42 {
print("equal")
} else {
print("unequal")
}
Version 2
import Foundation
protocol IntTransformable: Equatable {
func toInt() -> Int
}
extension String: IntTransformable {
func toInt() -> Int {
return 42
}
}
extension Int: IntTransformable {
func toInt() -> Int {
return self
}
}
extension IntTransformable {
// throws: Protocol 'IntTransformable' can only be used as a generic constraint because it has Self or associated type requirements
static func == (lhs: IntTransformable, rhs: IntTransformable) -> Bool {
return lhs.toInt() == rhs.toInt()
}
}
// throws: Ambiguous reference to operator function '=='
if "42" == 42 {
print("equal")
} else {
print("unequal")
}
You should use these two functions:
func ==(lhs: String, rhs: #autoclosure ()->Int) -> Bool {
guard let stringIntValue = Int(lhs) else { return false }
return stringIntValue == rhs()
}
func ==(lhs: Int, rhs: String) -> Bool {
guard let stringIntValue = Int(rhs) else { return false }
return lhs == stringIntValue
}
But if you really want to involve Protocols here, you should do this like:
extension IntTransformable {
static func ==<T: IntTransformable>(lhs: Self, rhs: T) -> Bool {
return lhs.toInt() == rhs.toInt()
}
}
Usage:
print( 42 == "42" )
print( "42" == 42 )
You're way overthinking this. There is no reason to use protocols here, and you really can't do it because protocols are not really types. Just write your operator(s) at top level:
func == (lhs: Int, rhs: String) -> Bool {
return lhs == Int(rhs)
}
func == (lhs: String, rhs: Int) -> Bool {
return Int(lhs) == rhs
}
Testing:
print(5 == "5") // true
create a string extension, like
public string ToInt(this int value)
{
// some conversion code
return ConvertedStringValue;
}
or you can use a uint.TryParse(string value, out uint output)
this statement will return true if conversion is successful.

generics struct implementing protocol with associated type

I am a bit stuck trying to define a container for my ui elements.
As I wanted something that encapsulates a non unique label, a value that can be any comparable object and a concept of being the preferred option I came up with the following protocol:
protocol OptionProtocol:Comparable {
associatedtype Key:Comparable
associatedtype Value:Comparable
var key:Key { get set }
var value:Value { get set }
var main:Bool { get set }
static func <(lhs: Self, rhs: Self) -> Bool
static func ==(lhs: Self, rhs: Self) -> Bool
}
extension OptionProtocol {
static func <(lhs: Self, rhs: Self) -> Bool {
let equalKeys = lhs.key == rhs.key
return equalKeys ? lhs.value < rhs.value : lhs.key < rhs.key
}
static func ==(lhs: Self, rhs: Self) -> Bool{
return (lhs.value == rhs.value) && (lhs.key == rhs.key)
}
}
Now I want to implement the protocol in a generic struct and I cant figure out how. What I want to do is
struct Option<Key, Value>: OptionProtocol {
var key:Key
var value:Value
var main:Bool
}
But the compiler complains that Type 'Option<Key, Value>' does not conform to protocol 'OptionProtocol'
Any pointer would be helpful
The answer was pretty simple. I needed to constraint Key and Value in the struct.
The following struct compiles as expected
struct Option<Key, Value>:OptionProtocol where Key:Comparable, Value:Comparable {
var key:Key
var value:Value
var main:Bool
}

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
}
```

Nested Swift class can't conform to Comparable

I am trying to implement a simple array based heap but running into issues with the nested Node class. I would like my nodes to be comparable, however, the compiler is complaining that the node class doesn't conform.
import Foundation
class Heap<E:Comparable> {
var heap = Array<Node<E>>()
init() { }
class Node<E:Comparable >:Comparable {
var key:E!
init(key:E) { self.key = key }
}
}
func < <E> (lhs:Heap<E>.Node<E>, rhs:Heap<E>.Node<E>) -> Bool {
return lhs.key < rhs.key }
func == <E> (lhs:Heap<E>.Node<E>, rhs:Heap<E>.Node<E>) -> Bool {
return lhs.key == rhs.key }
It complains because of the Node class doesn't conform the Comparable protocol.
protocol Comparable : _Comparable, Equatable {
func <=(lhs: Self, rhs: Self) -> Bool
func >=(lhs: Self, rhs: Self) -> Bool
func >(lhs: Self, rhs: Self) -> Bool
}
And one more thing, if you want to use the self.key to compare, then the type should conform Comparable protocol as well. Here's the sample:
class Node<E where E:Comparable>:Comparable {
var key:E!
init(key:E) { self.key = key }
}
func ==<E>(lhs: Node<E>, rhs: Node<E>) -> Bool {
return lhs.key == rhs.key
}
func <<E>(lhs: Node<E>, rhs: Node<E>) -> Bool {
return lhs.key < rhs.key
}