opaque型およびexistential型のオプショナルに対する改善された構文
Improved Syntax for Optionals of Opaque and Existential Types
このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら↗。
01 何が問題だったのか
Swiftでは、protocol P に対して不透明型 some P と明示的な存在型 any P を書くことができます。しかし、これらにオプショナルのシュガー(? や !)を組み合わせようとすると、括弧で包んで (some P)? や (any P)? と書かなければなりませんでした。
Int? や String? のようなオプショナル記法に慣れていると、自然に some P? や any P? と書きたくなりますが、これらの記法はこれまでエラーになっていました。パーサが some (P?) や any (P?) として解釈してしまい、some / any の後ろにはprotocolが来る必要があるのに P?(= Optional<P>)はprotocolではない、という理由で弾かれていたためです。
// 今までは括弧が必須
let x: (any P)?
let y: (some P)?
// これはエラー(some (P?) / any (P?) と解釈される)
let x: any P?
let y: some P?
将来的にSwiftがexistential typesで any の明示を必須にする方向に進むと、オプショナルの存在型を書くたびに括弧が増えてしまい、可読性や書き味を損ねる懸念がありました。some P? / any P? が「P に適合する不透明型/存在型のオプショナル」以外の意味を持つことは考えにくく、この直感的な書き方を素直に受け入れるパーサに変える必要がありました。
02 どのように解決されるのか
some P? を (some P)? と、any P? を (any P)? と同じ意味になるようパーサの優先順位を変更します。some / any の直後に続くprotocol型の末尾に付いた ? / ! を、some / any 全体を囲むように持ち上げて解釈するイメージです。
// 以下はすべて等価に書けるようになります
let a: any P? = nil // (any P)?
let b: some P? = nil // (some P)?
let c: any P! = nil // (any P)!
オプショナルの多重化にも対応し、some P?? は (some P)?? と解釈されます。また、暗黙アンラップドオプショナル(!)が許される位置では同じ規則で any P! も使えます。
protocol composition
protocol composition(P & Q)と組み合わせる場合は、これまでどおり composition を括弧で囲む必要があります。括弧で囲まれていれば新しい記法と併用でき、(any P & Q)? に加えて any (P & Q)? も書けるようになります。
// いずれも (any P & Q)? と同じ意味
let x: (any P & Q)? = nil
let y: any (P & Q)? = nil
一方で、括弧なしで any P & Q? と書くのは引き続きエラーです。新しいパースルール上は「P & Q に適合する存在型のオプショナル」と解釈することも技術的には可能ですが、postfixの ? が長めのprotocol compositionと any キーワードまでまとめて覆うのは読み手にとって紛らわしいため、あえて許容しない判断がされています。この場合はコンパイラが専用の診断を出し、any (P & Q)? のように括弧を補うfix-itを提示します。
protocol suppression(~)との組み合わせ
~Copyable のような protocol suppression と some / any とオプショナルが同時に並ぶ any ~Copyable? のような書き方にも対応します。パーサの優先順位としては、suppression の ~ はオプショナルの ?/! よりは弱いが protocol composition の & よりは強い位置に入ります。
Prec(?) > Prec(~) > Prec(&) > Prec({some, any})
この結果、any ~Copyable? は (any (~Copyable))? として解釈され、「Copyable を要求しない存在型のオプショナル」という直感どおりの意味になります。以前の解釈 any (~(Copyable?)) は、そもそも型として成立しないため実用上の後退はありません。
// いずれも (any ~Copyable)? と同じ意味
let x: (any ~Copyable)? = nil
let y: any ~Copyable? = nil
一方、some / any を付けずに素の ~Copyable? と書いた場合は、これまでどおり ~(Copyable?) としてパースされます。~Copyable? が式の文脈にも現れうる(配列リテラルの要素型など)ため、prefix 演算子よりも postfix 演算子を強く結合させるという基本ルールを曲げてまで解釈を変えることはしない、という整理です。~Copyable 系の suppression は既に any なしだと警告が出るため、実務上は any ~Copyable? を書けば十分です。
既存の any fix-it の更新
-enable-experimental-feature ExistentialAny 下で、protocol型をそのまま型として使っている箇所には「any P と書くべき」という警告とfix-itが出ます。これまでfix-itは (any P) と括弧付きで挿入されていましたが、単一のprotocol制約でオプショナル(もしくは暗黙アンラップドオプショナル)の場合は、新しい記法に合わせて括弧のない any P を挿入するようになります。
let x: P?
// 旧: fix-it は (any P)? を提案
// 新: fix-it は any P? を提案
既存コードはすべてそのまま有効で、(some P)? や (any P)? のように明示的に括弧を書いているスタイルもこれまでどおり使えます。型のマングリングも変わらないため、ABIや API resilience への影響もありません。