SE-0364の"retroactive"ってどういう意味?
レトロアクティブ適合とは
レトロアクティブ(retroactive)とは、「遡及的な」や「過去にさかのぼって適用される」という意味を持つ。プログラミングにおいては、既存の型やプロトコルに対して、後から適合(conformance)を追加する行為を指す。
具体的には、自分が所有していない型(例えば、標準ライブラリの型)に対して、自分が所有していないプロトコルへの適合を、自分のコード内で追加すること。これにより、その型が新たにプロトコルに適合したように振る舞うようになる。
// 標準ライブラリで定義されている型
struct ExternalType { /* ... */ }
// 標準ライブラリで定義されているプロトコル
protocol ExternalProtocol { /* ... */ }
// 自分のコードでレトロアクティブ適合を追加
extension ExternalType: ExternalProtocol {
// プロトコルの要件を実装
}
なぜ問題になるのか
レトロアクティブ適合は一見便利だが、いくつかの深刻な問題を引き起こす可能性がある。
将来の衝突の可能性
自分が所有していない型とプロトコルに対して適合を追加した場合、将来的にその型の所有者が同じプロトコル適合を公式に追加する可能性がある。その場合、どの適合が適用されるのか不明確になり、ビルドエラーや未定義の動作を招く。
// 自分のコードでの適合
extension Date: Identifiable {
var id: Double { self.timeIntervalSinceReferenceDate }
}
// 将来、Foundationが同じ適合を追加
extension Date: Identifiable {
var id: UUID { /* ... */ }
}
未定義の動作とデータ不整合
適合の競合により、異なる実装が使用される可能性がある。
影響範囲の拡大
ライブラリやフレームワークでレトロアクティブ適合を行うと、その影響がそのライブラリを利用する全てのクライアントに及ぶ。バイナリフレームワークの場合、クライアント側はその適合がどこから来たのかを知る手段がなく、予期せぬ動作を引き起こす。
SE-0364での警告導入
これらの問題を受けて、SE-0364ではレトロアクティブ適合に対する警告をコンパイラに導入する提案がなされた。警告により、開発者に対して潜在的な問題を知らせる目的がある。
根本的な解決策の例
型やプロトコルの所有者に適合を依頼
必要なプロトコル適合を公式に追加してもらうよう、型やプロトコルの所有者に提案する。これにより、将来の競合を防ぐことができる。
ラッパー型を作成
問題の型を内包する新しい型を作成し、その新しい型に対してプロトコル適合を実装する。
struct MyDate: Identifiable {
let date: Date
var id: Double { date.timeIntervalSinceReferenceDate }
}
まとめ
「retroactive」とは、遡及的な適合を意味し、既存の型やプロトコルに対して後から適合を追加する行為を指す。便利ではあるが、将来的な競合や未定義の動作など、深刻な問題を引き起こす可能性がある。SE-0364では、この問題に対処するために警告の導入が提案されている。
参考資料
Discussion
@retroactiveの例
retroactive = 再定義された(redefined), Swift Core Teamの実装の後に再定義された, 独自実装
遡及(過去に遡る)の意味は無い
e.g. a retroactive pay increase = a retro pay, 給与算定法再定義により増える賃金
次のコードを実行すると以下の警告が発生する。
警告: Swift Core Team(the owners of Swift)が実装を変えると正しく動作するとは限りません。
警告は@retroactiveアノテーションで(コメントを外せば)消える