Swift Digest
SE-0039 | Swift Evolution

Modernizing Playground Literals

Proposal
SE-0039
Authors
Erica Sadun
Review Manager
Chris Lattner
Status
Implemented (Swift 3.0)

01 何が問題だったのか

Xcode のプレイグラウンドには、色・画像・ファイル参照を値として扱えるよう、ソースコード中に埋め込める「プレイグラウンドリテラル」が用意されていました。エディタ上では見栄えのよい専用ビュー(色見本や画像サムネイル)として表示され、ドラッグ&ドロップで差し替えられる便利な機能です。この提案以前は、これらは次のような構文で書かれていました。

[#Color(colorLiteralRed: red, green: green, blue: blue, alpha: alpha)#]
[#Image(imageLiteral: localResourceNameAsString)#]
[#FileReference(fileReferenceLiteral: localResourceNameAsString)#]

この書き方には、Swift の表層として見たときに次のような問題がありました。

角括弧が配列リテラルと衝突する

リテラル全体を囲む [# ... #] という外殻は、配列リテラルの [ ... ] と字面が重なっており、パーサや読み手にとって「配列リテラルのつもりで読み始めたらプレイグラウンドリテラルだった」という余計な揺さぶりを生みます。言語仕様としての見通しがよくありません。

先頭大文字のキャメルケースが Swift らしくない

Color / Image / FileReference のように、リテラルを表すトークンが型名そのものに見える綴りになっていました。他の Swift のリテラル(truenil など)や既存の #available はすべて小文字で書かれており、大文字始まりのキャメルケースは浮いています。

引数ラベルの literal の位置がおかしい

colorLiteralRed:imageLiteral: のように、引数ラベル側に literal という単語が混ぜ込まれていました。しかし「リテラルである」のは引数そのものではなく、構築されるトークンの役割のはずです。単語の指す対象と、それが書かれる位置が噛み合っておらず、API として不自然でした。

同時期の # 接頭辞の新構文との足並みを揃えたい

ちょうど同じ時期に、#available#selector(SE-0022)、そしてソース位置系識別子の #file / #line(SE-0028)など、「コンパイラに特別扱いされる式」を # 接頭辞付きの小文字識別子で表す方向で Swift の語彙が整理されつつありました。プレイグラウンドリテラルだけが旧来の [# ... #] のまま取り残されているのは望ましくなく、このタイミングで新しい流儀に揃える必要がありました。

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

プレイグラウンドリテラルの構文を、# 接頭辞付きの小文字識別子による関数呼び出し風の形に書き換えます。新しい識別子は #colorLiteral / #imageLiteral / #fileLiteral の3つで、それぞれが色・画像・ファイル参照に対応します。

// Before
[#Color(colorLiteralRed: 1.0, green: 0.5, blue: 0.25, alpha: 1.0)#]
[#Image(imageLiteral: "icon.png")#]
[#FileReference(fileReferenceLiteral: "data.json")#]

// After
#colorLiteral(red: 1.0, green: 0.5, blue: 0.25, alpha: 1.0)
#imageLiteral(resourceName: "icon.png")
#fileLiteral(resourceName: "data.json")

新しい構文のかたち

3つのリテラルはいずれも、次のルールに従って整理されています。

  • 先頭の識別子は # 接頭辞を付けた小文字キャメルケース(#colorLiteral など)。既存の Swift のリテラルや #available と綴りの調子が揃います。
  • 引数ラベルは通常の小文字キャメルケース。色は red / green / blue / alpha、画像とファイルは resourceName に統一されます。
  • 引数側のラベルから literal という単語は消え、代わりに識別子の側(colorLiteral など)で「リテラルとしての役割」を表します。

色リテラルの各成分は、0.0 以上 1.0 以下の浮動小数点数リテラルで、画像・ファイルリテラルの resourceName は静的文字列リテラル(プレイグラウンド内のリソース名)です。これらは構文レベルで要求される形であり、任意の式を渡せるわけではありません。

背後のイニシャライザ

プレイグラウンドリテラルは、対応する型が特定のプロトコルに適合していれば自由に使えます。今回のリネームに合わせて、内部で呼び出されるイニシャライザのラベルも次のように整理されました。

protocol _ColorLiteralConvertible {
  init(colorLiteralRed red: Float, green: Float, blue: Float, alpha: Float)
}

protocol _ImageLiteralConvertible {
  init(imageLiteralResourceName path: String)
}

protocol _FileReferenceLiteralConvertible {
  init(fileReferenceLiteralResourceName path: String)
}

これらのイニシャライザのラベルがあえて colorLiteralRed: のように長いのは、型が「リテラル専用の振る舞い」を通常のイニシャライザから切り離して提供できるようにするためです。リテラル用のイニシャライザが型の一般的な API を汚染したり、他のイニシャライザと曖昧になったりするのを避ける狙いがあります。通常のコードで気にするのはあくまで #colorLiteral(red:green:blue:alpha:) という表層の構文であり、ラベルが冗長な内部プロトコル側を直接使うことはほとんどありません。

使いどころ

実用上は、これまで Xcode のプレイグラウンドで挿入されていたコードが、新しい構文で生成されるようになる、と捉えれば十分です。たとえば UI のプロトタイピング中に色や画像を差し替える場面で、次のように書けます。

let accent = #colorLiteral(red: 0.2, green: 0.6, blue: 0.9, alpha: 1.0)
let logo   = #imageLiteral(resourceName: "logo.png")
let sample = #fileLiteral(resourceName: "sample.json")

見た目は関数呼び出しに近い一方、Xcode 上では従来どおり色見本や画像サムネイルとしてインライン表示され、ドラッグ&ドロップでの差し替えにも対応します。既存の [# ... #] 形式で書かれたコードは、同等な #xxxLiteral(...) への機械的な置き換えで新構文に追従できます。