Renaming the DictionaryLiteral type to KeyValuePairs
01 何が問題だったのか
標準ライブラリには、["one": 1, "two": 2] のような辞書リテラル形式の記述を 順序を保ったまま、かつ重複キーも許す形で 受け取るための型として DictionaryLiteral<Key, Value> が用意されていました。Mirror のイニシャライザで子要素を渡すための引数型として使われているほか、@dynamicCallable の実装でも利用される予定(SE-0216)の型です。
public init<Subject>(
_ subject: Subject,
children: DictionaryLiteral<String, Any>,
displayStyle: Mirror.DisplayStyle? = nil,
ancestorRepresentation: Mirror.AncestorRepresentation = .generated
)
しかし、この名前は型の実体を正しく表していません。
- 辞書ではない: キーによる値の検索を提供しません。値を探すには全要素を走査する必要があります。
- リテラルではない: ソースコード中の固定値を表す型ではなく、辞書リテラル構文から初期化できる普通の値型です。
この型がやっていることは、実際には「キーと値の組のリストを、辞書リテラル構文 [:] から作れるようにする」ことだけです。[(Key, Value)] の薄いラッパに近く、順序の保持と重複キーの許容という点で素の配列とは異なる性質を持ちます。
名前が実体とずれていると、API を読む側が挙動を誤解しやすくなります。しかも Mirror がこの型に依存しており、ABI 安定化を前に型ごと削除する選択肢は取れません。名前のずれだけでも解消する必要がありました。
02 どのように解決されるのか
DictionaryLiteral を KeyValuePairs へリネームします。型の役割である「キーと値の組のリスト」をそのまま名前にした形です。
// Before (Swift 4.x まで)
public struct DictionaryLiteral<Key, Value> { ... }
// After (Swift 5.0 以降)
public struct KeyValuePairs<Key, Value> { ... }
Mirror のイニシャライザのシグネチャも、新しい名前に置き換わります。
public init<Subject>(
_ subject: Subject,
children: KeyValuePairs<String, Any>,
displayStyle: Mirror.DisplayStyle? = nil,
ancestorRepresentation: Mirror.AncestorRepresentation = .generated
)
辞書リテラル構文 [:] から直接初期化できる点は変わりません。順序の保持と重複キーの許容も従来通りです。
let pairs: KeyValuePairs<String, Int> = [
"one": 1,
"two": 2,
"one": 10, // 重複キーも許される
]
for (key, value) in pairs {
// 宣言順に "one": 1, "two": 2, "one": 10 が取り出される
print(key, value)
}
ソース互換性
旧名 DictionaryLiteral は typealias として残されるため、既存コードは引き続きコンパイルできます。将来的に非推奨化・削除される可能性はありますが、本 proposal の時点では typealias を介して利用可能な状態が保たれます。