Swift Digest
SE-0097 | Swift Evolution

Normalizing naming for “negative” attributes

Proposal
SE-0097
Authors
Erica Sadun
Review Manager
Chris Lattner
Status
Rejected

01 何が問題だったのか

Swift 2 までの言語機能には、属性のうちに no を接頭辞として付けた「否定」の名前を持つものがいくつか存在しました。代表的なのは次の二つです。

  • @noreturn — 関数がリターンしないこと(例外的な終了や fatalError のように制御が戻ってこないこと)を表す属性。
  • @noescape — クロージャのパラメータがその呼び出しのスコープを超えて保持されない(エスケープしない)ことを表す属性。
@noreturn func crash() {
  fatalError("unreachable")
}

func apply(@noescape _ body: () -> Void) {
  body()
}

これらはいずれも「〜しない」という性質を表す形容詞的な修飾ですが、名前の作り方を見ると、英語として修飾子の性質を表すなら本来は non-returning / non-escaping のように non- を使うのが自然です。no を使った場合、その後に続く動詞や名詞との境目でキャメルケースの凹凸が生まれやすく(noReturn / noEscape のように切らざるを得ない)、一語の形容詞として見たときの一体感を損ねます。

Swift は「属性は、修飾する対象の性質を表す一語の形容詞として組み立てる」という方針を取っており、@noreturn / @noescape だけがこの方針に沿っていない不揃いな状態でした。そのため、名前の付け方をそろえるために、これらの属性を「non- を接頭辞とした一語の形容詞」に置き換えることが検討されました。

02 どのように解決されるのか

この提案は Rejected(却下) となりました。@noescape@noreturn@nonescaping / @nonreturning に改名する変更は採用されず、別の方向で整理が進められました。

提案されていた内容(採用されなかったもの)

提案自体はごく小さく、次の2点の改名を行うだけの内容でした。

  • @noreturn@nonreturning に改名する。
  • @noescape@nonescaping に改名する。

もし採用されていれば、先ほどの例は次のように書くことになっていたはずです。

// 採択されていた場合のイメージ
@nonreturning func crash() {
  fatalError("unreachable")
}

func apply(@nonescaping _ body: () -> Void) {
  body()
}

既存コードは、属性名を機械的に置き換えるだけで移行できる想定でした。

却下の経緯と、その後の整理

レビューでは、「名前をそろえること自体には意味があるが、改名という形で解決するのが正しいのかは別の問題だ」という議論が中心となりました。特に @noreturn については、”point of no return”(引き返せない地点)のように英語の慣用表現としての “no return” が定着しており、@nonreturning よりも @noreturn のほうがむしろ読み手に意図が伝わりやすい、という指摘もありました。

また、これらの属性のうち、@noreturn@noescape はそれぞれ別の方向で置き換えが進められることになります。

  • @noreturn は、SE-0102 によって属性そのものが廃止され、戻り値の型を Never にする方式に置き換えられました。Never は値を生成できない空の型で、関数が正常に戻ってこないことを戻り値の型として表現できます。

    // 現在の Swift
    func crash() -> Never {
      fatalError("unreachable")
    }
    
  • @noescape は、SE-0103 によって扱いが反転しました。すなわち、クロージャのパラメータは既定で non-escaping となり、エスケープする場合にだけ @escaping を明示的に書く、という形に整理されました。否定形の属性を廃し、肯定形の属性だけを残す方針です。

    // 現在の Swift
    func apply(_ body: () -> Void) {           // 既定で non-escaping
      body()
    }
    
    func store(_ body: @escaping () -> Void) { // エスケープする場合だけ明示
      handler = body
    }
    

結果として、「no- 始まりの属性名をどうそろえるか」という問いは、SE-0097 が提案した形(non- への改名)ではなく、そもそも否定形の属性を言語から取り除く方向で解消されました。今日の Swift では @noreturn / @noescape のいずれも存在せず、Never@escaping の組み合わせで同等以上の表現ができるようになっています。