web-dev-qa-db-ja.com

プロトコル演算子をグローバル関数として実装する必要があるのはなぜですか?

私はこれに対する答えを見てきました Swift Equatable Protocol==メソッドがグローバルスコープでどのように宣言されなければならないかについて言及している質問。

Equatableを採用しない場合でも、==を宣言して、2つのタイプが等しいかどうかをテストできます。

// extension Foo: Equatable {}

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

struct Foo {
    let bar:Int
}

その実装をグローバルスコープで宣言する必要があるという事実により、偶発的および異なるEquatableが採用された場合でも、プロトコルから。

Equatableプロトコルは、私たちの型がプロトコルの必要なメソッドを実装したことをコンパイラーに(私たちと)安全に知らせるだけの構文糖衣構文以上のものはどうですか?

プロトコルであっても、演算子の実装をグローバルに宣言する必要があるのはなぜですか?これは、オペレーターが派遣される方法が異なるためですか?

30
user4151918

更新

Xcode 8ベータ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
}

元の

これは最近Swift-evolutionリストで議論されました(これまでのところ2016-01-31から2016-02-09) 構造体またはクラススコープでの演算子の宣言に関して、ChrisLattnerは次のように述べています。

はい、これは一般的に望ましい機能です(少なくとも対称演算子の場合)。これは、クラス宣言内で演算子を動的にディスパッチする場合にも役立ちます。ただし、名前の検索がこれでどのように機能するかを明確にする提案はないと思います。

そして後で(Haravikkに返信):

名前検索の問題は何ですか? Foo == Fooの演算子が複数の場所に存在する場合を意味しますか?

はい。名前検索には、シャドウイングと無効な複数定義ルールを定義する、明確に定義された検索順序が必要です。

個人的には、現在の状態に固執します。つまり、特定のクラス/構造体内の演算子の実装をグローバルに定義されているものとして扱い、同じシグネチャが複数回宣言されている場合はエラーをスローします。

演算子のインスタンスを定義できるようにするには複数のモジュールが必要であり、拡張機能に演算子が必要であり、他のメンバーと同様に機能するには遡及的な適合性が必要です。

22
rob mayoff

ドキュメント からの説明

演算子関数は、オーバーロードされる演算子と一致する関数名を持つグローバル関数として定義されます。

この関数は、ターゲットクラスまたは構造体のメソッドとしてではなく、グローバルに定義されているため、ターゲットクラスまたは構造体の既存のインスタンス間の中置演算子として使用できます。

コンクリート構造物の名前を置き換えました(Vector2D)一般式による引用ターゲットクラスまたは構造

7
vadian

その実装はグローバルスコープで宣言する必要があるという事実により、Equatableが採用されたとしても、プロトコルに付随し、プロトコルとは異なるように見えます。

これは、グローバル関数、クラスメソッド、インスタンスメソッドのいずれが必要かを問わず、すべてのプロトコルに当てはまります。それを必要とするプロトコルがあるかどうかに関係なく、いつでも物事を実装できます。

Equatableプロトコルは、私たちの型がプロトコルの必要なメソッドを実装したことをコンパイラーに(私たちと)安全に知らせるだけのシンタックスシュガー以上のものはどうですか?

それは砂糖ではありません。これがプロトコルの定義であり、プロトコルの要点です。これは、このタイプがそれに適用できるこれらのものを利用できることを示しています。

プロトコルであっても、演算子の実装をグローバルに宣言する必要があるのはなぜですか?これは、オペレーターが派遣される方法が異なるためですか?

それはSwift構文です。演算子関数の実装はグローバル関数である必要があります。Swiftチームから、これを変更して一貫性を高めることに関心がありましたが、今日、これはSwiftの仕組みです。

7
Rob Napier

Equatableの実装を手動で行う必要があったため、以前は多少砂糖でした。 Rob Napierは、それがプロトコルの目的であるという良い点を提起しましたが。

しかし、それはno長くなります。つまり、適合性だけでAutomated Synthesisが得られ、ボイラープレートは不要になります。コード。

struct Country: Equatable {
  let name: String
  let capital: String
  var visited: Bool
}

それでおしまい! compilerが残りを行います。

let france = Country(name: "France", capital: "Paris", visited: true)
let spain = Country(name: "Spain", capital: "Madrid", visited: true)
if france == spain { ... } // false

そうは言っても、必要に応じて、==関数の実装をオーバーライドできます。

詳細については、 ここ を参照してください

1
Honey

Swift標準ライブラリでは、 '=='演算子が定義されています。比較の一方または他方に結合性がなく、全体的に番号付きの優先順位がありますSwift演算子の順序。CMDでクリックするとこれを調べることができますSwift on'import Swift '

infix operator == {
    associativity none
    precedence 130
}

Intの場合、ライブラリにはすでにInt/Int8/Int16/Int32/Int64が同等として含まれています。

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

他の多くのタイプと同じように。

Intは拡張機能でハッシュ可能になります(そのプロトコルに準拠するには、アプリの実行ごとに変更されるvar hashValue:Int {get}を作成する必要があります。

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

演算子 '=='とプロトコルは、一般に、言語を拡張しているため、トップレベルのstruct/enum/classの外部で宣言する必要があります。今のところ、このようにしてSwift ;すべてのSwiftの演算子は、グローバルスコープで定義された属性です。

'Foo'には、関数を追加してEquatableを採用しました。

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

これは、プロトコルで「自己」ローカル変数を使用します。

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