| RFC-0040:ID 唯一性 | |
|---|---|
| 狀態 | 已接受 |
| 區域 |
|
| 說明 | FIDL 規格和前端編譯器目前會根據簡單的字串比較,將兩個 ID 視為不同。這項提案建議採用新的演算法,將繫結產生器進行的轉換納入考量。 |
| 作者 | |
| 提交日期 (年-月-日) | 2019-04-08 |
| 審查日期 (年-月-日) | 2019-05-14 |
"SnowFlake vs SNOW_FLAKE"
摘要
FIDL 規格和前端編譯器目前會根據簡單的字串比較,將兩個 ID 視為不同。這項提案建議採用新的演算法,將繫結產生器所做的轉換納入考量。
提振精神
語言繫結產生器會轉換 ID,以符合目標語言限制和樣式,並將多個 FIDL ID 對應至單一目標語言 ID。這可能會導致無法預期的衝突,而且在指定特定語言前不會顯示。
設計
這項提案建議對 FIDL ID 導入限制,現有程式庫不會違反這項限制。這項工具不會變更 FIDL 語言、IR (目前 1)、繫結、樣式指南或評分標準。
實務上,ID 由一系列連在一起的字詞組成。
常見的字詞合併方式為 CamelCase,其中從小寫到大寫的轉換是字詞界線,以及 snake_case,其中使用一或多個底線 (_) 分隔字詞。
ID 應轉換為標準形式,以利比較。這會是 lower_snake_case 表單,保留原始表單中的字詞分隔。如果字詞含有底線 (1)、從大寫轉換為小寫或數字 (2),以及從大寫轉換為小寫 (3),系統就會將字詞拆開。
在 FIDL 中,ID 必須使用原始形式。
因此,如果類型名為 FooBar,嘗試將其參照為 foo_bar 會發生錯誤。
有一個簡單的演算法可執行這項轉換,以下是 Python 2 的程式碼:
def canonical(identifier):
last = '_'
out = ''
for i, c in enumerate(identifier):
is_next_lower = i + 1 < len(identifier) and identifier[i+1].islower()
if c == '_':
if last != '_':
out += '_'
elif (((last.islower() or last.isdigit()) and c.isupper())
or (last != '_' and c.isupper() and is_next_lower)):
out += '_' + c.lower()
else:
out += c.lower()
last = c
return out
以下列舉幾個範例,以及在各種譯文語言中的可能翻譯:
| FIDL ID | 標準形式 | C++ | 荒漠油廠 | 查看 | Dart |
|---|---|---|---|---|---|
| foobar | foobar | foobar | foobar | Foobar | foobar_ |
| foo_bar | foo_bar | foo_bar | foo_bar | FooBar | fooBar_ |
| foo__bar | foo_bar | foo_bar | foo_bar | FooBar | fooBar_ |
| FooBar | foo_bar | foo_bar | foo_bar | FooBar | fooBar_ |
| fooBar | foo_bar | foo_bar | foo_bar | FooBar | fooBar_ |
| FOOBar | foo_bar | foo_bar | foo_bar | FooBar | fooBar_ |
導入策略
前端編譯器會更新,確保每個新 ID 的標準形式不會與任何其他 ID 的標準形式衝突。
下一版 FIDL IR 應以正式名稱而非原始名稱為基礎,但原始名稱會以宣告中的欄位形式提供。如果我們可以在產生的繫結中排除使用未修改的名稱,則可以從 IR 中捨棄原始名稱。
人體工學
這會將實際存在的 FIDL 語言限制編碼。
說明文件和範例
FIDL 語言說明文件會更新,說明這項限制。擴充後會納入上述「設計」部分的大多數內容。
因為這項提案只是編碼現有做法,範例和教學課程不會受到影響。
回溯相容性
如果現有的 FIDL 程式庫違反這項變更,就會違反我們的樣式指南,且無法與許多語言繫結搭配使用。這不會改變用於計算序數的 ID 形式。
效能
這對前端編譯器造成的負擔微乎其微。
安全性
沒有影響。
測試
我們會在 fidlc 中對標準化演算法實作進行廣泛測試。此外,也會有 fidlc 測試,確保在宣告衝突的 ID 時會偵測到錯誤,並確保必須使用原始名稱參照宣告。
缺點、替代方案和未知事項
其中一個選項是不採取任何行動。
一般來說,我們會在非 C++ 產生的繫結中,將這些問題視為建構失敗。
隨著 Rust 在 fuchsia.git 中越來越常使用,衝突問題滲透到其他花瓣的機率也隨之降低。而且這類問題相當罕見。
正規化演算法很簡單,但有一個不幸的失敗案例:UPPER_SNAKE_CASE 識別碼中的英數字混合字詞可能會中斷。例如 H264_ENCODER → h264_encoder,但 A2DP_PROFILE → a2_dp_profile。這是因為演算法會將數字視為小寫字母。我們必須在數字轉為字母時中斷,因為 H264Encoder 應正規化為 h264_encoder。如果 ID 沒有小寫字母,可能會特別處理 (只在底線處換行),但這會增加演算法的複雜度,或許也會增加心智模型複雜度。
標準形式可以表示為字詞清單,而非 lower_camel_case 字串。兩者是等效的,但實際上以字串形式管理會比較簡單。
我們可以在生成序數時使用 ID 的標準形式。 這樣做沒有明顯好處,卻會造成破壞性變更。 如果日後有打破序數的旗幟日,我們可能會考慮變更。
初步拒絕和二次審查
在 2019 年 4 月 18 日的首次審查中,這個 FTP 遭到拒絕,理由如下。
- 解決這類問題的兩種對立觀點。
- 盡可能模擬目標語言的限制,在 FIDL 中維持最大彈性,即使與建議的樣式不同也沒關係。這也是本 FTP 採用的方法。
- 優點:保留彈性,可供 Fuchsia 以外的 FIDL 最終用途使用,從程式設計語言的角度來看更純粹。
- 缺點:範圍規則較為複雜,且不會強制執行樣式,但建議使用 (例如透過 Linting)。可能會導致合作夥伴建構的 API 不符合我們想要的 Fuchsia 樣式指南 (因為他們不需要執行或遵守 Linting)。
- 直接在語言中強制執行樣式限制,即可消除這類問題。
- 優點:強制執行樣式、告知開發人員應如何處理,或不編譯。
- 缺點:在語言定義中加入樣式選擇,使用 FIDL 的新手開發人員需要克服更高的難度。
- → 我們拒絕了這項提案,並偏好直接在語言中強制執行樣式的方法。
- → 下一步是提出正式提案,以實現此目標,並釐清所有相關事宜 (例如,
uint8應為Uint8,vector<T>應為Vector<T>嗎?)
根據下列觀察結果,我們決定撤銷這項處置:
核心 API 現在以 FIDL 說明。因此我們在去年 10 月重新提出 ID 唯一性問題,並推翻原先的決定,允許「C 樣式」和「FIDL 樣式」共存。目前 fidl linter 會檢查這項內容。
我們發現其他用途會促使 FIDL 一般化傳輸,並可能連帶一般化本機樣式規則,以更妥善支援這些網域。
ID 衝突仍是個問題,而 FIDL 工具鍊中也明顯缺少目標語言限制的模擬功能。
既有技術和參考資料
在 proto3 中,系統會套用類似規則,為 JSON 編碼產生 lowerCamelCase 名稱。