some View
は、SE-0244で導入された不透明な結果タイプであり、Swift 5.1 with Xcode 11で使用できます。これは、「逆」の汎用プレースホルダーと考えることができます。
呼び出し元が満足する通常の一般的なプレースホルダーとは異なり:
protocol P {}
struct S1 : P {}
struct S2 : P {}
func foo<T : P>(_ x: T) {}
foo(S1()) // Caller chooses T == S1.
foo(S2()) // Caller chooses T == S2.
不透明な結果タイプは、実装によって満たされる暗黙的な汎用プレースホルダーであるため、次のように考えることができます。
func bar() -> some P {
return S1() // Implementation chooses S1 for the opaque result.
}
このように見えます:
func bar() -> <Output : P> Output {
return S1() // Implementation chooses Output == S1.
}
実際、この機能の最終的な目標は、このより明示的な形式で逆ジェネリックを許可することです-> <T : Collection> T where T.Element == Int
。これにより、制約を追加することもできます。詳細については、この投稿を参照してください。
これから取り除かなければならない主なことsome P
は、返す関数は、に準拠する特定の単一の具象型の値を返す関数であることP
です。関数内で異なる準拠タイプを返そうとすると、コンパイラエラーが発生します。
// error: Function declares an opaque return type, but the return
// statements in its body do not have matching underlying types.
func bar(_ x: Int) -> some P {
if x > 10 {
return S1()
} else {
return S2()
}
}
暗黙のジェネリックプレースホルダーは複数のタイプで満たすことができないため。
これは、返す関数とは対照的であるP
表現するために使用することができ、その両方を S1
し、S2
それが任意表すためP
適合値:
func baz(_ x: Int) -> P {
if x > 10 {
return S1()
} else {
return S2()
}
}
さて、不透明な結果型に-> some P
はプロトコルの戻り値型よりどのような利点があります-> P
か?
1. PATで不透明な結果タイプを使用できます
現在のプロトコルの主な制限は、PAT(タイプが関連付けられているプロトコル)を実際のタイプとして使用できないことです。これは、言語の将来のバージョンで解除される可能性のある制限ですが、不透明な結果タイプは事実上、単なる汎用プレースホルダーであるため、現在PATで使用できます。
つまり、次のようなことができます。
func giveMeACollection() -> some Collection {
return [1, 2, 3]
}
let collection = giveMeACollection()
print(collection.count) // 3
2.不透明な結果タイプには同一性があります
不透明な結果タイプは単一の具象タイプが返されることを強制するため、コンパイラーは、同じ関数への2つの呼び出しが同じタイプの2つの値を返す必要があることを認識しています。
つまり、次のようなことができます。
// foo() -> <Output : Equatable> Output {
func foo() -> some Equatable {
return 5 // The opaque result type is inferred to be Int.
}
let x = foo()
let y = foo()
print(x == y) // Legal both x and y have the return type of foo.
コンパイラは両方x
を認識しy
、同じ具象型を持っているため、これは合法です。これはの重要な要件で==
あり、両方のタイプのパラメータが必要ですSelf
。
protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
これは、どちらも具象適合型と同じ型である2つの値を期待することを意味します。Equatable
タイプとして使用できたとしても、次の例のように、2つの任意のEquatable
適合値を互いに比較することはできません。
func foo(_ x: Int) -> Equatable { // Assume this is legal.
if x > 10 {
return 0
} else {
return "hello world"
}
}
let x = foo(20)
let y = foo(5)
print(x == y) // Illegal.
コンパイラは、2つの任意のEquatable
値が同じ基本の具象型を持っていることを証明できないためです。
同様に、別の不透明型を返す関数を導入した場合:
// foo() -> <Output1 : Equatable> Output1 {
func foo() -> some Equatable {
return 5 // The opaque result type is inferred to be Int.
}
// bar() -> <Output2 : Equatable> Output2 {
func bar() -> some Equatable {
return "" // The opaque result type is inferred to be String.
}
let x = foo()
let y = bar()
print(x == y) // Illegal, the return type of foo != return type of bar.
両方があるため例は違法となりfoo
とbar
リターンsome Equatable
、その一般的なプレースホルダを「逆」Output1
とOutput2
、異なる種類によって満たすことができます。
3.不透明な結果タイプが一般的なプレースホルダーで構成されている
通常のプロトコルタイプの値とは異なり、不透明な結果タイプは通常の一般的なプレースホルダーで適切に構成されます。次に例を示します。
protocol P {
var i: Int { get }
}
struct S : P {
var i: Int
}
func makeP() -> some P { // Opaque result type inferred to be S.
return S(i: .random(in: 0 ..< 10))
}
func bar<T : P>(_ x: T, _ y: T) -> T {
return x.i < y.i ? x : y
}
let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Legal, T is inferred to be the return type of makeP.
2つの値の基礎となる具象型が異なる可能性があるため、がmakeP
返さP
れただけでは機能しませんP
でした。たとえば、次のようになります。
struct T : P {
var i: Int
}
func makeP() -> P {
if .random() { // 50:50 chance of picking each branch.
return S(i: 0)
} else {
return T(i: 1)
}
}
let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Illegal.
具象型よりも不透明な結果型を使用する理由
この時点で、あなたは自分で考えているかもしれません、なぜ単に次のようにコードを書いてはいけません:
func makeP() -> S {
return S(i: 0)
}
まあ、不透明な結果型を使用すると、S
によって提供されるインターフェイスのみを公開することで、型を実装の詳細にすることができP
、関数に依存するコードを壊すことなく、後で具象型を柔軟に変更できるようになります。
たとえば、次のものを置き換えることができます。
func makeP() -> some P {
return S(i: 0)
}
と:
func makeP() -> some P {
return T(i: 1)
}
を呼び出すコードを壊すことなくmakeP()
。
この機能の詳細については、言語ガイドの不透明タイプのセクションとSwiftの進化案を参照してください。