Почему оператор протокола должен быть реализован как глобальная функция?

Я видел ответ на этот Swift Equableable Protocol вопрос, который упоминает, как метод == должен быть объявлен в глобальной области.

Если я не принимаю Equatable, я все равно могу объявить == для проверки равенства между двумя моими типами.

// extension Foo: Equatable {}

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

struct Foo {
    let bar:Int
}

Тот факт, что его реализация должна быть объявлена ​​в глобальной области, делает ее случайной и отличной от протокола, даже если был принят Equatable.

Как протокол Equatable ничего больше, чем синтаксический сахар, который просто позволяет (нам и) компилятору безопасно знать, что наш тип реализовал требуемый метод протокола?

Почему реализация оператора должна быть объявлена ​​во всем мире даже для протокола? Это связано с каким-то другим способом отправки оператора?

Ответ 1

UPDATE

Из примечаний к выпуску Xcode 8 beta 4:

Операторы могут быть определены внутри типов или расширений. Например:

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

Такие операторы должны быть объявлены как static (или внутри класса, class final) и иметь одинаковые как их глобальные коллеги. В рамках этого изменения требования оператора, заявленные в протоколы также должны быть явно объявлены static:

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

ORIGINAL

Это обсуждалось недавно в списке быстрой эволюции (с 2016-01-31 по 2016-02-09). Крис Лэттнер сказал, что объявлять операторов в области структуры или класса:

Да, это вообще желательная особенность (по крайней мере для симметричных операторов). Также было бы здорово получить динамическую диспетчеризацию операторов в объявлениях классов. Я не думаю, что у нас есть четкое предложение о том, как работает поиск имени с этим.

И позже (отвечая на Харавикк):

Каковы проблемы поиска имени? Вы имеете в виду случаи, когда оператор для Foo == Foo существует в нескольких местах?

Да. Поиск имени должен иметь четко определенный порядок поиска, который определяет теневое и неверное несколько правил определения.

Лично Id просто придерживайтесь того, что у нас есть сейчас, i.e- обрабатывает реализации оператора внутри определенного класса/структуры как глобально   в любом случае, и выдавать ошибку, если та же подпись объявлена   более одного раза.

Нам нужно несколько модулей, чтобы иметь возможность определять экземпляры оператора, нам нужны операторы в расширениях, и нам нужны ретроактивные совместимости с работой, как и с любым другим членом.

Ответ 2

Объяснение из documentation

Операторная функция определяется как глобальная функция с функцией имя, которое соответствует оператору, который будет перегружен.

Функция определяется глобально, а не как метод на целевого класса или структуры, чтобы он мог использоваться как оператор инфикса между существующими экземплярами целевого класса или структуры.

Я заменил имя конкретной структуры (Vector2D) в цитате универсальным выражением целевого класса или структуры

Ответ 3

Тот факт, что его реализация должна быть объявлена ​​в глобальной области, делает ее случайной и отличной от протокола, даже если Equatable был принят.

Это верно для каждого протокола, независимо от того, нужны ли ему глобальные функции, методы класса или методы экземпляра. Вы всегда можете реализовать вещи независимо от того, существует ли требуемый протокол.

Как протокол Equatable ничего больше, чем синтаксический сахар, который просто позволяет (нам и) компилятору безопасно знать, что наш тип реализовал требуемый метод протокола?

Это не сахар. Это определение протокола и всей точки протоколов. Он сообщает вам, что этот тип имеет доступ к этим вещам, которые могут быть применены к нему.

Почему реализация оператора должна быть объявлена ​​во всем мире даже для протокола? Это связано с каким-то другим способом отправки оператора?

Потому что этот синтаксис Swift. Реализации функций оператора должны быть глобальными функциями. Команда Swift заинтересована в том, чтобы изменить ее, чтобы сделать ее более последовательной, но сегодня это именно то, как работает Swift.

Ответ 4

В стандартной библиотеке Swift определен оператор '=='. Он не имеет ассоциативности для одной стороны или другого сравнения и имеет пронумерованный приоритет в общем порядке оператора Swift. Вы можете проверить это, если вы CMD-щелкните Swift на 'import Swift'

infix operator == {
    associativity none
    precedence 130
}

Для Int's библиотека уже имеет Int/Int8/Int16/Int32/Int64 в качестве Equatable:

public func ==(lhs: Int, rhs: Int) -> Bool

как и для других типов.

Int сделан Hashable в расширении (где для соответствия этому протоколу вам необходимо создать var hashValue: Int {get}, который изменяется для каждого выполнения приложения:

extension Int : Hashable {
    public var hashValue: Int { get }
}

Оператор '==' и протоколы, как правило, должны быть объявлены за пределами struct/enum/class на верхнем уровне, потому что вы расширяете язык и на данный момент, как быстро скомпилирован; все операторы Swift являются атрибутами, определенными в глобальной области.

Для вашего "Foo" вы приняли Equatable, добавив func:

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

который использует локальные переменные "Self" в протоколе:

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