Swift Digest
SE-0023 | Swift Evolution

API Design Guidelines

Proposal
SE-0023
Authors
Dave Abrahams, Doug Gregor, Dmitri Gribenko, Ted Kremenek, Chris Lattner, Alex Migicovsky, Max Moiseev, Ali Ozer, Tony Parker
Review Manager
Doug Gregor
Status
Implemented (Swift 3.0)

01 何が問題だったのか

Swift 1〜2の時点では、標準ライブラリも、Clang ImporterがObjective-Cから持ち込むAPIも、それぞれ独自の命名慣習で書かれていました。Objective-CはCoding Guidelines for Cocoaに沿う一方、Swift標準ライブラリには明文化された指針がなく、サードパーティのライブラリはその都度、言語機能(強い型付け、デフォルト引数、引数ラベル、オーバーロードなど)とどう付き合うかを自分で判断する必要がありました。

その結果として生じていた問題は、おおむね次のとおりです。

ライブラリごとに「Swiftらしさ」の基準がばらばら

同じような操作でも、ライブラリによってメソッド名の付け方(動詞形か動名詞形か、前置詞を名前に埋め込むか引数ラベルにするか、型名を名前に含めるか等)が違い、呼び出し側のコードが統一感を欠いていました。Swiftは型情報から多くのことを推測できる言語ですが、その前提を活かすかどうかの判断が各ライブラリ任せになっていました。

標準ライブラリ自身が「お手本」になれていない

標準ライブラリはほぼすべてのSwiftコードが参照する存在ですが、そこでの命名にも統一基準がなく、サードパーティのライブラリ作者が参考にすべき基準点として機能していませんでした。たとえば非破壊的な操作に動詞形(sort())が使われていたり、戻り値の型から明らかな語(minElement()Element)が名前に残っていたりといった不整合がありました。これらの具体的な改修はSE-0006で扱われますが、そのためにも、改修の基準となる文章が先に必要でした。

Objective-CからのインポートにSwift側の基準がない

Objective-CのAPIをSwiftに取り込む際に「Swiftらしい」形へどう整えるかも、準拠すべき文章がないため判断できませんでした。たとえば stringByTrimmingCharactersInSet(_:)trimming(_:) に丸めるといった変換を、どのルールに沿って、どこまで自動で行うのかを決めるには、まずSwift側の命名原則が言語化されている必要がありました。この自動変換規則の整備はSE-0005で扱われますが、その拠り所となる文章の不在が障害になっていました。

このように、個別のAPI変更に着手する前の段階で、Swiftコミュニティ全体が共有できるAPI設計の指針そのものが欠けていた、というのが本提案が取り組んだ問題です。

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

Swift 3に向けて、Swiftらしいライブラリ設計の基準を明文化した「Swift API Design Guidelines」が策定されました。本提案自体は、このガイドライン文書を正式にSwiftプロジェクトの指針として採択するものです。ガイドラインはswift.org上で公開・保守され、標準ライブラリの改修(SE-0006)やObjective-C APIの自動変換規則(SE-0005)はいずれもこの文書を拠り所として進められます。

ガイドラインの中心的な指針は、次のようなものです。

使用時点での明快さを最優先にする

APIは定義されるよりもはるかに多くの回数、呼び出し側で読まれます。そのため、定義の簡潔さよりも、使用時点での読みやすさを優先します。短さそのものが価値なのではなく、必要な情報が引数ラベルや名前に過不足なく現れている状態を目指します。

曖昧にならない範囲で、余分な語を落とす

Swiftは型情報から多くのことを保証できるため、名前側で型や役割を繰り返す必要はありません。逆に、型だけでは伝わらない役割(何を、どこへ、どのように)は、名前や引数ラベルに残します。

// 冗長: Element は戻り値の型から明らか
xs.minElement()

// 必要な情報だけを残す
xs.min()

メソッドや関数の呼び出しを英文のように読ませる

メソッド名とその引数ラベルを合わせて読んだときに、英文として自然な句になるように設計します。前置詞はメソッド名に埋め込まず、最初の引数ラベルに切り出します。

// Before
x.removeAtIndex(i)
["a", "b"].joinWithSeparator(", ")

// After
x.remove(at: i)
["a", "b"].joined(separator: ", ")

副作用の有無を文法で表す

副作用を伴う(mutatingな)操作は動詞の原形、副作用のない変換を返す操作は -ed / -ing 形に揃えます。ペアで両方提供する場合、sort() / sorted()reverse() / reversed() のように語形で使い分けます。両方を自然な語形で表しにくいときは、非破壊側に form プレフィックス(union / formUnion)を使う、といった規約も定めます。

// 破壊的: 自身を変更する
var ys = xs
ys.sort()

// 非破壊的: 新しい値を返す
let zs = xs.sorted()

プロパティとメソッドの使い分け

計算量が小さく副作用のないアクセサはプロパティに、計算や副作用を伴うものはメソッドに寄せます。利用者が「これは値の一部として読む」のか「これは何かを実行する呼び出しだ」のかを、括弧の有無で素直に判断できるようにします。

命名の表記規則

型・プロトコルは大文字始まり(UpperCamelCase)、それ以外(変数・関数・メソッド・プロパティ・enumケース・静的プロパティなど)は小文字始まり(lowerCamelCase)で統一します。頭字語(URLHTML など)も、位置によって URL / url のように大文字小文字を揃えます。

Boolean の名前は「主張」として読ませる

真偽値のプロパティやメソッドは、レシーバに対する主張として読めるよう命名します。プロパティなら isEmpty、メソッドなら contains(_:) のように、if x.isEmpty { ... }if xs.contains(x) { ... } と自然な英文になる形にします。

ドキュメンテーションコメントを活用する

公開APIには、1行のサマリに続けて必要に応じて追加の節を持つドキュメンテーションコメント(///)を付けます。サマリは宣言型(三人称単数現在、命令形ではない)で書きます。

/// Returns a new string created by concatenating the elements of the
/// sequence, adding the given separator between each element.
func joined(separator: String = "") -> String

ジェネリクスや演算子などの追加指針

ジェネリクスの型パラメータ名は役割を表す名詞(ElementKey など)にし、意味がないときだけ TU のような1文字を使う、オーバーロードは曖昧さを生まない範囲で活用し、引数ラベルで意味を担保する、といった細かな指針もあわせて示されます。演算子は標準的な数学記法と一致する場合に限って使い、メソッドで済むところを無理に演算子化しないこと、クロージャパラメータの命名や、定数・シングルトンの扱いなども同じ文書にまとめられています。

位置づけと今後

SE-0023で採択されたガイドラインは、以降のSwift本体および標準ライブラリのAPI変更の判断基準となり、サードパーティのライブラリでも事実上の標準として参照されるようになります。文書自体はswift.orgで継続的に更新され、具体的な推奨は時代に合わせて改訂されますが、「使用時点での明快さを優先する」「型で保証できることを名前で繰り返さない」「呼び出しが英文のように読めるよう引数ラベルを設計する」といった中心的な思想は、その後のSwiftのAPI設計を一貫して支えています。