RFC-0040:ID 唯一性 | |
---|---|
狀態 | 已接受 |
區域 |
|
說明 | FIDL 規格和前端編譯器目前會根據簡單的字串比較,將兩個 ID 視為不同的 ID。這項研究提出了新演算法,可考量繫結產生器所做的轉換。 |
作者 | |
提交日期 (年-月-日) | 2019-04-08 |
審查日期 (年-月-日) | 2019-05-14 |
"SnowFlake vs SNOW_FLAKE"
摘要
FIDL 規格和前端編譯器目前會根據簡單的字串比較,將兩個 ID 視為不同的 ID。這項工具提出了新的演算法,可考量繫結產生器所做的轉換。
提振精神
語言繫結產生器會轉換 ID,以符合目標語言限制和樣式,將多個 FIDL ID 對應至單一目標語言 ID。這可能會導致非預期的衝突,除非指定特定語言,否則無法看見。
設計
這項提案建議針對 FIDL ID 導入限制,且沒有任何現有程式庫違反這項限制。但不會變更 FIDL 語言、IR (但 1)、繫結、樣式指南或評分標準。
在實際應用中,ID 是由一系列連結在一起的字詞組成。常見的字詞連接方式是 CamelCase
,其中從小寫轉換為大寫的字母是字詞邊界,以及 snake_case
,其中使用一或多個底線 (_
) 分隔字詞。
識別碼應轉換為標準形式,以利比較。這會是 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_ |
導入策略
前端編譯器將進行更新,以便檢查每個新識別碼的標準形式是否與任何其他識別碼的標準形式衝突。
下一個版本的 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 檢查器檢查。
我們發現其他用途會推動 FIDL 將傳輸方式泛用化,並可能使用本機樣式規則來進一步支援這些網域。
識別符衝突仍是個問題,而模擬目標語言限制是 FIDL 工具鍊中已知的差距。
既有技術與參考資料
在 proto3 中,系統會套用類似的規則,產生 JSON 編碼的 lowerCamelCase
名稱。