Why Must a Protocol Operator Be Implemented as a Global Function

Why must a protocol operator be implemented as a global function?

UPDATE

From the Xcode 8 beta 4 release notes:

Operators can be defined within types or extensions thereof. For example:

struct Foo: Equatable {
let value: Int
static func ==(lhs: Foo, rhs: Foo) -> Bool {
return lhs.value == rhs.value
}
}

Such operators must be declared as static (or, within a class, class final), and have the same
signature as their global counterparts. As part of this change, operator requirements declared in
protocols must also be explicitly declared static:

protocol Equatable {
static func ==(lhs: Self, rhs: Self) -> Bool
}

ORIGINAL

This was discussed on the swift-evolution list recently (2016-01-31 through 2016-02-09 so far). Here's what Chris Lattner said, regarding declaring operators in a struct or class scope:

Yep, this is a generally desirable feature (at least for symmetric operators). This would also be great to get dynamic dispatch of operators within class declarations. I don’t think we have a firm proposal nailing down how name lookup works with this though.

And later (replying to Haravikk):

What are the name lookup issues? Do you mean cases where an operator for Foo == Foo exists in more than one location?


Yes. Name lookup has to have a well defined search order, which
defines shadowing and invalid multiple definition rules.

Personally I’d just stick with what we have now, i.e- treat operator implementations within a specific class/struct as being globally
defined anyway and throw an error if the same signature is declared
more than once.


We need multiple modules to be able to define instances of an
operator, we need operators in extensions, and we need retroactive
conformance to work, as with any other member.

I don`t know why error happen

Try having a look at this answer

Your == method should not be added in an extension, it should be added globally. Even though it might seem to be a static method, it should still be declared as a global function. You can find a discussion about the details here.

Now...if you had declared your Post as a struct or a class then yes, you could have added a static == method inside your struct/class. However, you have declared a protocol and a protocol can not have any methods.

This answer shows you how to have a protocol implement Equatable.

Armed with all that we can implement your Post protocol and have it implement Equatable like so:

protocol Post: Equatable {
var referenceIndex: Int { get set}
var likeCount: Int { get set}
var likeStatus: Bool { get set}
var commentCount: Int { get set}
var commentStatus: Bool { get set}
}

func ==<T : Post>(lhs: T, rhs: T) -> Bool {
return lhs.referenceIndex == rhs.referenceIndex
}

And then, to prove that things are working:

struct SomePost: Post {
var referenceIndex: Int
var likeCount: Int
var likeStatus: Bool
var commentCount: Int
var commentStatus: Bool
}

let somePost1 = SomePost(referenceIndex: 1, likeCount: 1, likeStatus: true, commentCount: 1, commentStatus: true)
let somePost2 = SomePost(referenceIndex: 2, likeCount: 1, likeStatus: true, commentCount: 1, commentStatus: true)
let somePost3 = SomePost(referenceIndex: 1, likeCount: 1, likeStatus: true, commentCount: 1, commentStatus: true)

somePost1 == somePost2 //false
somePost1 == somePost3 //true

Hope that helps you

Why a equatable protocol needs to be defined outside the class?

Mainly because it's a function and not a method. Functions are independent on classes so it makes no sense to scope them inside class declarations.

The protocol could have been designed with methods, e.g. using a obj1.isEqualTo(obj2) method but a function is less sensitive to nil problems. If obj1 were nil, the method would fail.

Swift ambiguous overloaded + operator

The + operator shouldn't be inside the extension, but rather a global func. Replace your definition of + :

extension Number {    
static func + (lhs: Number, rhs: Number) -> Number {
return SimpleNumber(lhs.doubleValue + rhs.doubleValue)
}
}

with simply

func + (lhs: Number, rhs: Number) -> Number {
return SimpleNumber(lhs.doubleValue + rhs.doubleValue)
}

and it will work. I guess what you have done is created a static function Number.+, not the global function + that you are using...

Swift Equatable Protocol

Move this function

func == (lhs: Cookie, rhs: Cookie) -> Bool {
return lhs.column == rhs.column && lhs.row == rhs.row
}

Outside of the cookie class. It makes sense this way since it's overriding the == operator at the global scope when it is used on two Cookies.

understanding protocol extensions in swift

As touched on in this Q&A, there's a difference between operator overloads implemented as static members and operator overloads implemented as top-level functions. static members take an additional (implicit) self parameter, which the compiler needs to be able to infer.

So how is the value of self inferred? Well, it has to be done from either the operands or return type of the overload. For a protocol extension, this means one of those types needs to be Self. Bear in mind that you can't directly call an operator on a type (i.e you can't say (Self.<)(a, b)).

Consider the following example:

protocol Value {
func get() -> Float
}

extension Value {
static func < (a: Value, b: Value) -> Bool {
print("Being called on conforming type: \(self)")
return a.get() < b.get()
}
}

struct S : Value {
func get() -> Float { return 0 }
}

let value: Value = S()
print(value < value) // Ambiguous reference to member '<'

What's the value of self in the call to <? The compiler can't infer it (really I think it should error directly on the overload as it's un-callable). Bear in mind that self at static scope in a protocol extension must be a concrete conforming type; it can't just be Value.self (as static methods in protocol extensions are only available to call on concrete conforming types, not on the protocol type itself).

We can fix both the above example, and your example by defining the overload as a top-level function instead:

protocol Value {
func get() -> Float
}

func < (a: Value, b: Value) -> Bool {
return a.get() < b.get()
}

struct S : Value {
func get() -> Float { return 0 }
}

let value: Value = S()
print(value < value) // false

This works because now we don't need to infer a value for self.

We could have also given the compiler a way to infer the value of self, by making one or both of the parameters take Self:

protocol Value {
func get() -> Float
}

extension Value {
static func < (a: Self, b: Self) -> Bool {
print("Being called on conforming type: \(self)")
return a.get() < b.get()
}
}

struct S : Value {
func get() -> Float { return 0 }
}

let s = S()
print(s < s)

// Being called on conforming type: S
// false

The compiler can now infer self from the static type of operands. However, as said above, this needs to be a concrete type, so you can't deal with heterogenous Value operands (you could work with one operand taking a Value; but not both as then there'd be no way to infer self).


Although note that if you're providing a default implementation of <, you should probably also provide a default implementation of ==. Unless you have a good reason not to, I would also advise you make these overloads take homogenous concrete operands (i.e parameters of type Self), such that they can provide a default implementation for Comparable.

Also rather than having get() and set(to:) requirements, I would advise a settable property requirement instead:

// Not deriving from Comparable could be useful if you need to use the protocol as
// an actual type; however note that you won't be able to access Comparable stuff,
// such as the auto >, <=, >= overloads from a protocol extension.
protocol Value {
var floatValue: Double { get set }
}

extension Value {

static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.floatValue == rhs.floatValue
}

static func < (lhs: Self, rhs: Self) -> Bool {
return lhs.floatValue < rhs.floatValue
}
}

Finally, if Comparable conformance is essential for conformance to Value, you should make it derive from Comparable:

protocol Value : Comparable {
var floatValue: Double { get set }
}

You shouldn't need a min(of:and:) function in either case, as when the conforming type conforms to Comparable, it can use the top-level min(_:_:) function.

Swift 4.0 Difference between implementing custom operator inside a type as a type method and in global scope as a global function

With

infix operator >>> : FunctionCompositionPrecedence
extension Parser {
public static func >>> <A, B> (lhs: Parser<A>, rhs: Parser<B>)
-> Parser<(A,B)> {
return lhs.followed(by: rhs)
}
}

You've provided no way for the compiler to infer the generic placeholder Result on calling the operator (really I think the compiler should error here rather than on usage). Remember that static methods on generic types are called on specialisations of those types; the placeholders must be satisfied (as they're accessible at static scope).

So to directly answer

what is the difference between the first implementation and the second one.

The main difference is that as a static member, you have the additional generic placeholder Result that needs to be satisfied; as a top-level function, you don't have that.

So, if you want to keep >>> as a static method, you'll want to use the Result placeholder in the signature of your operator implementation such that the compiler can infer its type on usage, for example:

infix operator >>> : FunctionCompositionPrecedence
extension Parser {

public static func >>> <B> (
lhs: Parser, rhs: Parser<B>
) -> Parser<(Result, B)> {
return lhs.followed(by: rhs)
}
}

Now Result can be inferred from the type of the argument passed as the lhs of the operator (Parser is syntactic sugar for Parser<Result> in this context).

Note you'll face a similar problem with

extension Parser {
public static func apply <A, B> (_ lhs: Parser<(A)->B>, _ rhs: Parser<A>) -> Parser<B> {
return (lhs >>> rhs).map{(arg) -> B in let (f, x) = arg; return f(x)}
}
}

in that you'll need to explicitly satisfy the Result placeholder when calling; although the type used to satisfy it won't actually be used by the method.

Better would be to use the Result placeholder in the signature to allow the compiler to infer it at the call-site:

extension Parser {

public static func apply<Arg>(
_ lhs: Parser<(Arg) -> Result>, _ rhs: Parser<Arg>
) -> Parser<Result> {

return (lhs >>> rhs).map { arg -> Result in
let (f, x) = arg
return f(x)
}
}
}

// ...

let p = Parser<(String) -> String> { input in ({ $0 + input }, "hello") }
let p1 = Parser { ($0, "") }

let p2 = Parser.apply(p, p1)
print(p2.run(" world") as Any) // Optional(("hello world", ""))

Or, better still, as an instance method:

extension Parser {

public func apply<A, B>(with rhs: Parser<A>) -> Parser<B>
where Result == (A) -> B {

return (self >>> rhs).map { arg -> B in
let (f, x) = arg
return f(x)
}
}
}

// ...

let p = Parser<(String) -> String> { input in ({ $0 + input }, "hello") }
let p1 = Parser { ($0, "") }

let p2 = p.apply(with: p1)
print(p2.run(" world") as Any) // Optional(("hello world", ""))

Implement Equatable for custom private class - Swift

You need to also declare your == operator function as private for this to work. Functions are by default scoped as internal, and you can't have an internal method with privately scoped parameters or return type.

private class Foo : Equatable {
var bar = ""
}

private func ==(lhs: Foo, rhs: Foo) -> Bool {
return lhs.bar == rhs.bar
}


Related Topics



Leave a reply



Submit