Operator overloading not yet supported? - operator-overloading

According to the Swift Programming Guide, operator overloading is allowed and actually quite versatile. However, I have been unable to get it working in the playground.
For example, the Equatable protocol wants this: func ==(lhs:Self, rhs:Self) -> Bool
Let's say I make a simple Location3D struct:
struct Location3D
{
var x : Double
var y : Double
var z : Double
}
Now I want this Location3D to implement the Equatable protocol, so I add it along with this method:
func ==(lhs: Self, rhs: Self) -> Bool
{
return lhs.x == rhs.x &&
lhs.y == rhs.y &&
lhs.z == rhs.z
}
I get the compiler error of operators are only allowed at global scope. Huh?
So I tried adding #infix to the function, moving the function to an extension, changing the type to a class instead... all to no avail.
Am I missing something? How are you supposed to implement Equtable and Comparable when operators don't seem to work?

You need to override the == operator in the global scope but with your type for the arguments.
In this case it means you declare your struct to conform to the protocol and then simply implement the function outside it's scope.
struct Location3D : Equatable {
// ...
}
func ==(lhs: Location3D, rhs: Location3D) -> Bool {
// ...
}
See the library reference for further discussion:
https://developer.apple.com/documentation/swift/equatable

Related

How to implement a default method for a custom infix operator in a protocol extension

I'm trying to implement a custom comparison infix operator ==^ as a slimmed down version of the standard equality operator ==
My app is mostly protocol oriented and therefor I'm trying to implement the default method static func ==^ inside the protocol extension. However when I make my class conform to the protocol I'm getting a Type 'MySourceNode' does not conform to protocol 'SourceNodeType' error and Xcode offers me to add the static func ==^ protocol stub.
My question is, how do I properly write the default implementation inside the protocol extension?
I've tried to find answers on SO but most of them are older and only talk about generic methods that are defined outside the protocol extension. But that doesn't seem to work in my case.
Here is a playground file with a simplified version of my protocol. There is a bit of background info after the code. If something is unclear please let me know in the comments and I'll update my question accordingly.
import Foundation
import SpriteKit
infix operator ==^: ComparisonPrecedence
protocol SourceNodeType: SKShapeNode {
var constrainZRotation: SKConstraint! { get set }
func setConstraints()
static func ==^ (lhs: SourceNodeType, rhs: SourceNodeType) -> Bool
}
extension SourceNodeType {
func setConstraints() {
print("Setting constraints")
}
static func ==^ (lhs: SourceNodeType, rhs: SourceNodeType) -> Bool {
lhs.frame == rhs.frame &&
lhs.position == rhs.position &&
lhs.constrainZRotation == rhs.constrainZRotation
}
}
class MySourceNode: SKShapeNode, SourceNodeType {
var constrainZRotation: SKConstraint!
}
To explain the reason behind this custom infix operator. I need it because I'm comparing subclasses of SpriteKit's SKShapeNodes with each other quite often. But I don't want every variable in the class like the accessibility label or name to be compared as I'm changing these values when I added the nodes to the scene, which is partially after the comparison.
I realized putting the func ==^ method outside of the protocol and only implementing it with the SourceNodeType protocol as rhs and lhs types was exactly what I needed!
This answer solved my problem:
https://stackoverflow.com/a/41390111/12764795
Maybe someone could confirm that this is the current and correct way to go?
import Foundation
import SpriteKit
infix operator ==^: ComparisonPrecedence
func ==^ (lhs: SourceNodeType, rhs: SourceNodeType) -> Bool {
lhs.frame == rhs.frame &&
lhs.position == rhs.position &&
lhs.constrainZRotation == rhs.constrainZRotation
}
protocol SourceNodeType: SKShapeNode {
var constrainZRotation: SKConstraint! { get set }
func setConstraints()
}
extension SourceNodeType {
func setConstraints() {
print("Setting constraints")
}
}
class MySourceNode: SKShapeNode, SourceNodeType {
var constrainZRotation: SKConstraint!
}
let lhsSourceNode = MySourceNode(circleOfRadius: 10)
var rhsSourceNode = MySourceNode(circleOfRadius: 10)
print(lhsSourceNode ==^ rhsSourceNode) // TRUE
rhsSourceNode.name = "Name used for testing"
print(lhsSourceNode ==^ rhsSourceNode) // TRUE
rhsSourceNode.position.y += 100
print(lhsSourceNode ==^ rhsSourceNode) // FALSE
The thing is, you just can't satisfy this kind of protocol requirements :
static func ==^ (lhs: SourceNodeType, rhs: SourceNodeType) -> Bool
I could not find a clear explanation in Swift Documentation but if you try to make a type conform to that protocol it won't compile and it's not supposed to.
My question is, how do I properly write the default implementation inside the protocol extension?
If you want to use parameters of type SourceNodeType, using a free function is the most straightforward way but removing the operator requirement silences the compiler error ie :
protocol SourceNodeType: SKShapeNode {
var constrainZRotation: SKConstraint! { get set }
func setConstraints()
}
until you try to use your operator :
let node = MySourceNode()
let otherNode = MySourceNode()
node ==^ otherNode // Generic parameter 'Self' could not be inferred
You can still work around this issue by declaring :
extension SourceNodeType {
static func ==^ (lhs: Self, rhs: SourceNodeType) -> Bool {
lhs.frame == rhs.frame &&
lhs.position == rhs.position &&
lhs.constrainZRotation == rhs.constrainZRotation
}
}
it will work as expected but you still will not be able to declaring it as a protocol requirement because it will trigger :
Protocol 'SourceNodeType' can only be used as a generic constraint because it has Self or associated type requirements
Since rhs is of type SourceNodeType.

Using diff in an array of objects that conform to a protocol

I'm experimenting with using Composition instead of Inheritance and I wanted to use diff on an array of objects that comply with a given protocol.
To do so, I implemented a protocol and made it comply with Equatable:
// Playground - noun: a place where people can play
import XCPlayground
import Foundation
protocol Field:Equatable {
var content: String { get }
}
func ==<T: Field>(lhs: T, rhs: T) -> Bool {
return lhs.content == rhs.content
}
func ==<T: Field, U: Field>(lhs: T, rhs: U) -> Bool {
return lhs.content == rhs.content
}
struct First:Field {
let content:String
}
struct Second:Field {
let content:String
}
let items:[Field] = [First(content: "abc"), Second(content: "cxz")] // đź’Ą boom
But I've soon discovered that:
error: protocol 'Field' can only be used as a generic constraint because it has Self or associated type requirements
I understand why since Swift is a type-safe language that needs to be able to know the concrete type of these objects at anytime.
After tinkering around, I ended up removing Equatable from the protocol and overloading the == operator:
// Playground - noun: a place where people can play
import XCPlayground
import Foundation
protocol Field {
var content: String { get }
}
func ==(lhs: Field, rhs: Field) -> Bool {
return lhs.content == rhs.content
}
func ==(lhs: [Field], rhs: [Field]) -> Bool {
return (lhs.count == rhs.count) && (zip(lhs, rhs).map(==).reduce(true, { $0 && $1 })) // naive, but let's go with it for the sake of the argument
}
struct First:Field {
let content:String
}
struct Second:Field {
let content:String
}
// Requirement #1: direct object comparison
print(First(content: "abc") == First(content: "abc")) // true
print(First(content: "abc") == Second(content: "abc")) // false
// Requirement #2: being able to diff an array of objects complying with the Field protocol
let array1:[Field] = [First(content: "abc"), Second(content: "abc")]
let array2:[Field] = [Second(content: "abc")]
print(array1 == array2) // false
let outcome = array1.diff(array2) // đź’Ą boom
error: value of type '[Field]' has no member 'diff'
From here on, I'm a bit lost to be honest. I read some great posts about type erasure but even the provided examples suffered from the same issue (which I assume is the lack of conformance to Equatable).
Am I right? And if so, how can this be done?
UPDATE:
I had to stop this experiment for a while and totally forgot about a dependency, sorry! Diff is a method provided by SwiftLCS, an implementation of the longest common subsequence (LCS) algorithm.
TL;DR:
The Field protocol needs to comply with Equatable but so far I have not been able to do this. I need to be able to create an array of objects that comply to this protocol (see the error in the first code block).
Thanks again
The problem comes from a combination of the meaning of the Equatable protocol and Swift’s support for type overloaded functions.
Let’s take a look at the Equatable protocol:
protocol Equatable
{
static func ==(Self, Self) -> Bool
}
What does this mean? Well it’s important to understand what “equatable” actually means in the context of Swift. “Equatable” is a trait of a structure or class that make it so that any instance of that structure or class can be compared for equality with any other instance of that structure or class. It says nothing about comparing it for equality with an instance of a different class or structure.
Think about it. Int and String are both types that are Equatable. 13 == 13 and "meredith" == "meredith". But does 13 == "meredith"?
The Equatable protocol only cares about when both things to be compared are of the same type. It says nothing about what happens when the two things are of different types. That’s why both arguments in the definition of ==(::) are of type Self.
Let’s look at what happened in your example.
protocol Field:Equatable
{
var content:String { get }
}
func ==<T:Field>(lhs:T, rhs:T) -> Bool
{
return lhs.content == rhs.content
}
func ==<T:Field, U:Field>(lhs:T, rhs:U) -> Bool
{
return lhs.content == rhs.content
}
You provided two overloads for the == operator. But only the first one has to do with Equatable conformance. The second overload is the one that gets applied when you do
First(content: "abc") == Second(content: "abc")
which has nothing to do with the Equatable protocol.
Here’s a point of confusion. Equability across instances of the same type is a lower requirement than equability across instances of different types when we’re talking about individually bound instances of types you want to test for equality. (Since we can assume both things being tested are of the same type.)
However, when we make an array of things that conform to Equatable, this is a higher requirement than making an array of things that can be tested for equality, since what you are saying is that every item in the array can be compared as if they were both of the same type. But since your structs are of different types, you can’t guarantee this, and so the code fails to compile.
Here’s another way to think of it.
Protocols without associated type requirements, and protocols with associated type requirements are really two different animals. Protocols without Self basically look and behave like types. Protocols with Self are traits that types themselves conform to. In essence, they go “up a level”, like a type of type. (Related in concept to metatypes.)
That’s why it makes no sense to write something like this:
let array:[Equatable] = [5, "a", false]
You can write this:
let array:[Int] = [5, 6, 7]
or this:
let array:[String] = ["a", "b", "c"]
or this:
let array:[Bool] = [false, true, false]
Because Int, String, and Bool are types. Equatable isn’t a type, it’s a type of a type.
It would make “sense” to write something like this…
let array:[Equatable] = [Int.self, String.self, Bool.self]
though this is really stretching the bounds of type-safe programming and so Swift doesn’t allow this. You’d need a fully flexible metatyping system like Python’s to express an idea like that.
So how do we solve your problem? Well, first of all realize that the only reason it makes sense to apply SwiftLCS on your array is because, at some level, all of your array elements can be reduced to an array of keys that are all of the same Equatable type. In this case, it’s String, since you can get an array keys:[String] by doing [Field](...).map{ $0.content }. Perhaps if we redesigned SwiftLCS, this would make a better interface for it.
However, since we can only compare our array of Fields directly, we need to make sure they can all be upcast to the same type, and the way to do that is with inheritance.
class Field:Equatable
{
let content:String
static func == (lhs:Field, rhs:Field) -> Bool
{
return lhs.content == rhs.content
}
init(_ content:String)
{
self.content = content
}
}
class First:Field
{
init(content:String)
{
super.init(content)
}
}
class Second:Field
{
init(content:String)
{
super.init(content)
}
}
let items:[Field] = [First(content: "abc"), Second(content: "cxz")]
The array then upcasts them all to type Field which is Equatable.
By the way, ironically, the “protocol-oriented” solution to this problem actually still involves inheritance. The SwiftLCS API would provide a protocol like
protocol LCSElement
{
associatedtype Key:Equatable
var key:Key { get }
}
We would specialize it with a superclass
class Field:LCSElement
{
let key:String // <- this is what specializes Key to a concrete type
static func == (lhs:Field, rhs:Field) -> Bool
{
return lhs.key == rhs.key
}
init(_ key:String)
{
self.key = key
}
}
and the library would use it as
func LCS<T: LCSElement>(array:[T])
{
array[0].key == array[1].key
...
}
Protocols and Inheritance are not opposites or substitutes for one another. They complement each other.
I know this is probably now what you want but the only way I know how to make it work is to introduce additional wrapper class:
struct FieldEquatableWrapper: Equatable {
let wrapped: Field
public static func ==(lhs: FieldEquatableWrapper, rhs: FieldEquatableWrapper) -> Bool {
return lhs.wrapped.content == rhs.wrapped.content
}
public static func diff(_ coll: [Field], _ otherCollection: [Field]) -> Diff<Int> {
let w1 = coll.map({ FieldEquatableWrapper(wrapped: $0) })
let w2 = otherCollection.map({ FieldEquatableWrapper(wrapped: $0) })
return w1.diff(w2)
}
}
and then you can do
let outcome = FieldEquatableWrapper.diff(array1, array2)
I don't think you can make Field to conform to Equatable at all as it is designed to be "type-safe" using Self pseudo-class. And this is one reason for the wrapper class. Unfortunately there seems to be one more issue that I don't know how to fix: I can't put this "wrapped" diff into Collection or Array extension and still make it support heterogenous [Field] array without compilation error:
using 'Field' as a concrete type conforming to protocol 'Field' is not supported
If anyone knows a better solution, I'm interested as well.
P.S.
In the question you mention that
print(First(content: "abc") == Second(content: "abc")) // false
but I expect that to be true given the way you defined your == operator

Why does operator in Protocol `need more context`?

I have a protocol that only requires a > function. When I try to compare two objects that comform to the protocol I gives me a compiler error with the message "Type of expression is ambiguous without more context". I'd rather not require the function isGreaterThan(...) (or something) if I can avoid it, I'd rather stick to what people are used to using to compare (>).
So I have 2 questions. Why can't I do this? Is there a better way to do this, or a good workaround?
Thanks!
protocol Compare {
func >(lhs: Compare, rhs: Compare) -> Bool
}
class TheClass {
func hey(aCompare: Compare, theCompare: Compare) {
if aCompare > theCompare {
print("aCompare is greater than theCompare")
}
}
}
Rather than using the Protocol in the > operator, I should be using Self like Apple's Equatable and Comparable protocols.
protocol Compare {
func >(lhs: Self, rhs: Self) -> Bool
}
class TheClass {
func hey(aCompare: Compare, theCompare: Compare) {
if aCompare > theCompare {
print("aCompare is greater than theCompare")
}
}
}
Now compiles.. Thanks #jrturton and #originaluser2

compare two generic objects on swift 2.0

I have to establish whether the current value is equal to the given comparing value.
public static func Is<TBaseValue, TComparingValue>(baseValue: TBaseValue, comparingValue: TComparingValue) -> Bool
{
return baseValue == comparingValue
}
I almost try this
public static func Is<TBaseValue: Comparable, TComparingValue: Comparable>(baseValue: TBaseValue, comparingValue: TComparingValue) -> Bool
{
return baseValue==comparingValue
}
and this
public static func Is<TBaseValue: Equatable, TComparingValue: Equatable>(baseValue: TBaseValue, comparingValue: TComparingValue) -> Bool
{
return baseValue == comparingValue
}
but always I have the same result Binary operator cannot be applied....
Equatable doesn't exactly mean that. Think about things that are equatable - Int, for instance. 2 == 2 makes sense. 2 == 3 will return false - but it still makes sense. Now, think of something else that's Equatable - String, for instance.
"abc" == "abc" // true
"acb" == "abc" // false
That's fine - but what about this:
"abc" == 4
An Equatable thing is Equatable to itself - not to everything else. In your example, you're comparing two different types - TBaseValue, TComparingValue.
If you want to compare any values you can use overloading:
static func Is<T: Equatable>(first: T, second: T) -> Bool {
return first == second
}
static func Is<T1, T2>(first: T1, second: T2) -> Bool {
return false
}
So the most appropriate function gets called automatically. If the first function cannot be called with the passed parameters than the second one gets called anyhow.
This works in Swift 2.0.
func Is<Any : Equatable>(l: Any, _ r: Any) -> Bool {
return l == r
}
Is("a", "a") // true
Is("a", "b") // false
Is(1, 1) // true
It also would work with AnyObject.
For comparing two generics, you can declare the generics such that, types, that are capable to be in the place of your generic type, should conform to Comparable protocol.
struct Heap<T: Comparable>{
var heap = [T]()
mutating func insert(element: T){
heap.append(element)
var index = heap.count - 1
heapify(childIndex: index)
}}
Now we will be able to do:-
if heap[parentIndex] < heap[childIndex] {
//Your code
}
How this works?
As we know, conforming to a protocol means implementing all the required methods in that protocol. Comparable protocol has got all the comparison methods as required parameters, and any type that is implementing Comparable will be able to do a comparison.
Happy coding using Generics

Cannot invoke 'filter' with an argument list of type '((_) -> _)'

Sounds ridiculous, but I'm unable to fix this piece of code:
self.runningScripts.filter({ $0 != scriptRunner })
No matter how I write the closure I always get this error:
Cannot invoke 'filter' with an argument list of type '((_) -> _)'
runningScripts is defined like this:
var runningScripts = [ScriptRunner]()
and ScriptRunner is a Swift class (does not inherit from NSObject)
I'm using nearly the same in many other places without problems. Any suggestions?
You can get that error if you didn't make ScriptRunner conform to Equatable:
class ScriptRunner : Equatable {
// the rest of your implementation here
}
func ==(lhs: ScriptRunner, rhs: ScriptRunner) -> Bool {
return ... // change this to whatever test that satisfies that lhs and rhs are equal
}
I needed an explicit cast like this:
#NSManaged private var storage: [String]
private var objects: Set<String>?
func remove(element:String) {
initSetIfNeeded()
if(objects!.contains(element)) {
objects!.remove(element)
storage = storage.filter({($0 as NSObject) !== (element as NSObject)}) // Explicit cast here!!
}
}