bound generic typesへのエクステンション
Extensions on bound generic types
このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら↗。
01 何が問題だったのか
Swift では、ジェネリック型の型引数を指定する際は山括弧で書くのが一般的で、例えば Array<String> のように書きます。型エイリアスや、SE-0346 で導入された primary associated type の束縛でも同じ書き方が使えます。
typealias StringArray = Array<String>
extension StringArray { ... } // OK
protocol Collection<Element> {
associatedtype Element
}
extension Collection<String> { ... } // OK
ところが、ジェネリック型を直接 extension で拡張しようとすると、この山括弧構文は使えず、次のようなエラーになります。
extension Array<String> { ... }
// error: Constrained extension must be declared on the unspecialized generic type 'Array' with constraints specified by a 'where' clause
同じ意味を表現するには、where 句を使って次のように書く必要がありました。
extension Array where Element == String { ... }
言語のほとんどあらゆる場所で Array<String> と書けるのに、Array<String> を拡張するときだけ書けないというのは一貫性に欠け、「なぜ extension Array<Int> は通らないのか」という疑問がフォーラムでも繰り返し投稿されるなど、学習者にとって分かりにくい制限となっていました。
02 どのように解決されるのか
ジェネリック型を extension で拡張する際にも、型引数を山括弧で書いたり、[String] や Int? といったシュガー表記を使えるようにします。次の 3 つの宣言は、いずれも同じ型に対する extension を表します。
extension Array where Element == String { ... }
extension Array<String> { ... }
extension [String] { ... }
型引数の指定方法
ジェネリック型名のあとに、カンマ区切りの型引数リストを山括弧で続けて書きます。これは where 句で同型制約(same-type requirement)を書くのと等価です。
struct Pair<T, U> {}
extension Pair<Int, String> {}
// 次と等価:
// extension Pair where T == Int, U == String {}
型引数リストを書く場合は、型パラメータをすべて指定しなければなりません。一部だけを束縛したい場合は従来どおり where 句を使います。
extension Pair<Int> {}
// error: Generic type 'Pair' specialized with too few type parameters (got 1, but expected 2)
extension Pair where T == Int {} // OK
型引数には具体型のみを書けます。型プレースホルダ _ は使えません。
extension Pair<Int, _> {} // error: Cannot extend a type that contains placeholders
これは、通常 _ が「この位置の型を推論してほしい」という意味で使われるのに対し、extension での _ だと「その位置を制約しないまま残す」という別の意味になりかねず、同じ記号が文脈によって異なる意味を持ってしまうのを避けるためです。
また、型引数の名前解決は extension の外側のスコープで行われるため、拡張対象のジェネリック型が持つ型パラメータ名を型引数リストの中に書くことはできません。
extension Array<Element> {} // error: Cannot find type 'Element' in scope
シュガー表記
Array<T> や Optional<T> のようにシュガー表記を持つ型については、extension でもそのシュガーが使えます。
extension [String] { ... } // Array<String> を拡張
extension String? { ... } // Optional<String> を拡張
03 今後の見通し
本 Proposal は型引数リストをあくまで同型制約(same-type requirement)のシュガーとして扱っており、エクステンション側で新しい型パラメータを導入することはできません。Future Directions として、これを発展させた「parameterized extension」が示されていますが、あくまで構想であり実現を約束するものではありません。
parameterized extension が実現すれば、extension キーワードの直後に型パラメータリストを書くことで、より柔軟なエクステンションを宣言できるようになる可能性があります。例えば、Optional を要素型に持つ Array だけを拡張する、といった書き方が考えられます。
extension <Wrapped> Array<Optional<Wrapped>> { ... }
extension <Wrapped> [Wrapped?] { ... }
some を使ったショートハンドで、型パラメータに適合制約を付ける形のエクステンションも構想されています。
extension Array<some Equatable> { ... }
extension [some Equatable] { ... }
さらに、extension キーワード直後に型パラメータリストを書く形は、タプルのような構造的な型に対するエクステンションとも自然に組み合わせられます。たとえば 2 要素タプル全般や、可変長ジェネリックを使ったすべてのタプル型に対する適合の付与なども候補として挙げられています。
extension <T, U> (T, U) { ... }
extension <T...> (T...): Hashable where T: Hashable { ... }