Skip to content

Kotlin 演進原則

務實演進的原則

NOTE

語言設計一經定案,

但這塊石頭是相當柔軟的,

只要付出一些努力,我們就可以在日後重塑它。

Kotlin 設計團隊

Kotlin 旨在成為程式設計師的務實工具。當談到語言演進時,其務實的本質體現在以下原則中:

  • 使語言與時俱進。

  • 與用戶保持持續的反饋循環。

  • 讓用戶能輕鬆舒適地更新到新版本。

由於這對於理解 Kotlin 如何向前發展至關重要,讓我們詳細闡述這些原則。

使語言與時俱進。我們意識到系統會隨著時間累積遺留問題。曾經是尖端技術的事物,今天可能已經過時。我們必須讓語言不斷演進,以使其與用戶的需求保持相關,並符合他們的期望。這不僅包括新增功能,也包括逐步淘汰不再建議用於生產環境且已成為遺留的功能。

舒適的更新。不相容的變更,例如從語言中移除某些內容,如果處理不當,可能會導致從一個版本遷移到下一個版本時遇到痛苦。我們將始終提前宣布此類變更,將內容標記為已棄用,並在變更發生前提供自動遷移工具。等到語言變更時,我們希望世界上大部分的程式碼都已更新,因此在遷移到新版本時不會遇到任何問題。

反饋循環。經歷棄用週期需要付出巨大的努力,因此我們希望盡量減少未來會進行的不相容變更數量。除了運用我們最佳的判斷力之外,我們相信在現實生活中試用事物是驗證設計的最佳方式。在將事物定案之前,我們希望它們經過實戰驗證。這就是為什麼我們利用一切機會在語言的生產版本中提供設計的早期版本,但處於某種預穩定 (pre-stable) 狀態:Experimental、Alpha 或 Beta。此類功能不穩定,可以隨時變更,選擇使用它們的用戶明確表示他們已準備好處理未來的遷移問題。這些用戶提供了寶貴的反饋,我們收集這些反饋以迭代設計並使其穩固。

不相容變更

如果從一個版本更新到另一個版本時,某些原本可以運作的程式碼不再運作,這就是語言中的不相容變更(有時稱為「破壞性變更」)。在某些情況下,「不再運作」的確切含義可能會引起爭論,但它絕對包括以下內容:

  • 原本可以正常編譯和執行的程式碼現在被錯誤拒絕(在編譯或連結時)。

    這包括移除語言結構和新增限制。

  • 原本正常執行的程式碼現在拋出例外。

屬於「灰色區域」的較不明顯情況包括:以不同方式處理邊緣情況、拋出與之前不同類型的例外、僅透過反射才能觀察到的行為變更、修改未記錄或未定義的行為、重新命名二進位成品等等。有時此類變更至關重要,會顯著影響遷移體驗;有時則微不足道。

一些絕對不是不相容變更的例子包括:

  • 新增警告。

  • 啟用新的語言結構或放寬現有語言結構的限制。

  • 變更私有/內部應用程式介面 (API) 及其他實作細節。

「使語言與時俱進」和「舒適的更新」原則表明,不相容變更是有時是必要的,但應謹慎引入。我們的目標是讓用戶提前了解即將發生的變更,讓他們能夠舒適地遷移程式碼。

理想情況下,每次不相容變更都應透過問題程式碼中報告的編譯時警告(通常稱為棄用警告)進行宣布,並輔以自動遷移輔助工具。因此,理想的遷移工作流程如下:

  • 更新到版本 A(變更在此處宣布)

    • 查看有關即將發生的變更的警告

    • 在工具的幫助下遷移程式碼

  • 更新到版本 B(變更在此處發生)

    • 完全看不到任何問題

實際上,有些變更無法在編譯時準確檢測,因此無法報告警告,但至少用戶會透過版本 A 的發布說明得知版本 B 即將發生變更。

處理編譯器錯誤

編譯器是複雜的軟體,儘管開發人員付出了最大的努力,它們仍然存在錯誤。那些導致編譯器本身故障、報告虛假錯誤或生成明顯失敗程式碼的錯誤,儘管令人惱火且常常令人尷尬,但很容易修復,因為這些修復不構成不相容變更。其他錯誤可能導致編譯器生成不正確的程式碼但不會失敗:例如,遺漏原始碼中的某些錯誤或僅生成錯誤的指令。此類錯誤的修復在技術上是不相容變更(某些程式碼過去可以正常編譯,但現在不行了),但我們傾向於盡快修復它們,以防止不良的程式碼模式在用戶程式碼中傳播。在我們看來,這支持了「舒適的更新」原則,因為更少的用戶有機會遇到問題。當然,這僅適用於在發布版本中出現後很快發現的錯誤。

決策制定

JetBrains 是 Kotlin 的原始創作者,在社群的幫助下並與 Kotlin 基金會 合作推動其發展。

Kotlin 程式語言的所有變更都由首席語言設計師(目前是 Michail Zarečenskij)監督。首席設計師對所有與語言演進相關的事項擁有最終決定權。此外,對完全穩定組件的不相容變更必須由 Kotlin 基金會下指定的語言委員會(目前由 Jeffrey van Gogh、Werner Dietl 和 Michail Zarečenskij 組成)批准。

語言委員會就將進行哪些不相容變更以及應採取哪些確切措施來使用戶更新盡可能順暢做出最終決定。在這樣做時,它依賴一套語言委員會指南

語言與工具發布

版本號如 2.0.0 的穩定發布通常被視為語言發布,帶來語言上的重大變更。通常,我們會在語言發布之間發布版本號為 x.x.20工具發布

工具發布帶來工具更新(通常包括功能)、效能改進和錯誤修復。我們努力使這些版本彼此相容,因此編譯器的變更主要是優化和警告的增減。預穩定功能可以隨時新增、移除或變更。

語言發布通常會新增功能,並可能移除或變更先前已棄用的功能。功能從預穩定過渡到穩定也發生在語言發布中。

EAP 版本

在發布語言和工具發布的穩定版本之前,我們會發布一些稱為 EAP(搶先體驗預覽版)的預覽版本,這讓我們能夠更快地迭代並從社群收集反饋。語言發布的 EAP 通常會產生稍後將被穩定編譯器拒絕的二進位檔,以確保二進位格式中可能存在的錯誤不會在預覽期之後繼續存在。最終發布候選版本通常沒有此限制。

預穩定功能

根據上述「反饋循環」原則,我們公開迭代我們的設計,並發布語言版本,其中某些功能具有預穩定狀態,並且預期會變更。此類功能可以隨時新增、變更或移除,恕不另行通知。我們盡最大努力確保預穩定功能不會被不知情的用戶意外使用。此類功能通常需要在程式碼或專案配置中進行某種形式的明確選擇啟用。

Kotlin 語言功能可以具有以下狀態之一:

  • 探索與設計。我們正在考慮將新功能引入語言。

    這涉及討論它將如何與現有功能整合、收集使用案例,以及評估其潛在影響。

    我們需要用戶就此功能將解決的問題和它處理的使用案例提供反饋。

    如果可能,我們也會盡力估計這些使用案例和問題發生的頻率。

    通常,想法會記錄為 YouTrack 問題,討論會在那裡繼續進行。

  • KEEP 討論。我們相當確定該功能應該添加到語言中。

    我們旨在在一份名為 KEEP 的文件中提供動機、使用案例、設計及其他重要細節。

    我們期望用戶的反饋主要集中在討論 KEEP 中提供的所有資訊。

  • 預覽中。功能原型已準備就緒,您可以透過功能特定的編譯器選項啟用它。

    我們尋求您對該功能的使用體驗的反饋,包括它整合到您的程式碼庫中的容易程度、它與現有程式碼的互動方式,以及任何整合開發環境 (IDE) 支援問題或建議。

    該功能的設計可能會根據反饋顯著改變,或者可能被完全撤銷。當一個功能處於預覽中時,它具有穩定性等級

  • 穩定。該語言功能現在是 Kotlin 語言中的一等公民。

    我們保證其向後相容性,並將提供工具支援。

  • 撤銷。我們已撤銷提案,將不會在 Kotlin 語言中實作該功能。

    如果某個功能不適合 Kotlin,我們可能會撤銷處於預覽中的功能。

查看 Kotlin 語言提案及其狀態的完整列表

不同組件的狀態

了解更多關於 Kotlin 中不同組件(例如 Kotlin/JVM、JS 和 Native 編譯器以及各種函式庫)的穩定性狀態

函式庫

語言若沒有其生態系統,就什麼也不是,因此我們格外注意確保函式庫的順暢演進。

理想情況下,函式庫的新版本可以作為舊版本的「直接替換」。這意味著升級二進位依賴項不應破壞任何東西,即使應用程式沒有重新編譯(這在動態連結下是可能的)。

一方面,為了實現這一點,編譯器必須在單獨編譯的約束下提供一定的應用程式二進位介面 (ABI) 穩定性保證。這就是為什麼語言中的每個變更都從二進位相容性的角度進行審查。

另一方面,很多取決於函式庫作者小心謹慎地判斷哪些變更是安全的。因此,函式庫作者理解原始碼變更如何影響相容性並遵循某些最佳實踐以保持其函式庫的應用程式介面 (API) 和應用程式二進位介面 (ABI) 穩定至關重要。以下是我們在從函式庫演進角度考慮語言變更時做出的一些假設:

  • 函式庫程式碼應始終明確指定公共/受保護函數和屬性的返回類型,

    從而永遠不依賴於公共應用程式介面 (API) 的類型推斷。類型推斷的細微變更可能導致返回類型無意中改變,

    從而導致二進位相容性問題。

  • 同一函式庫提供的重載函數和屬性應本質上做相同的事情。

    類型推斷的變更可能導致在呼叫處已知更精確的靜態類型,

    從而導致重載解析的變更。

函式庫作者可以使用 @Deprecated@RequiresOptIn 註解來控制其應用程式介面 (API) 表面的演進。請注意,@Deprecated(level=HIDDEN)

可用於即使對於從應用程式介面 (API) 中移除的宣告也能保持二進位相容性。

此外,按照慣例,命名為「internal」的套件不被視為公共應用程式介面 (API)。所有位於名為「experimental」的套件中的應用程式介面 (API) 都被視為預穩定,並且可以隨時變更。

我們根據上述原則演進穩定平台的 Kotlin 標準函式庫 (kotlin-stdlib)。其應用程式介面 (API) 契約的變更遵循與語言本身變更相同的程序。

編譯器選項

編譯器接受的命令列選項也是一種公共應用程式介面 (API),它們也受到相同的考量。支援的選項(那些沒有「-X」或「-XX」前綴的選項)只能在語言發布中新增,並且在移除之前應妥善棄用。「-X」和「-XX」選項是實驗性的,可以隨時新增和移除。

相容性工具

隨著遺留功能的移除和錯誤的修復,原始語言發生了變更,未經妥善遷移的舊程式碼可能不再編譯。正常的棄用週期提供了舒適的遷移時間,即使該週期結束且變更在穩定版本中發布,仍然有一種方法可以編譯未遷移的程式碼。

相容性選項

我們提供 -language-version X.Y-api-version X.Y 選項,使新版本在相容性目的下模擬舊版本的行為。為了給您更多遷移時間,除了最新的穩定版本之外,我們還支援前三個語言和應用程式介面 (API) 版本。

積極維護的程式碼庫可以盡快從錯誤修復中受益,而無需等待完整的棄用週期完成。目前,此類專案可以啟用 -progressive 選項,即使在工具發布中也能啟用此類修復。

所有選項都可以在命令列中以及 GradleMaven 中使用。

二進位格式的演進

與最壞情況下可以手動修復的原始碼不同,二進位檔很難遷移,這使得向後相容性在二進位檔的情況下至關重要。對二進位檔的不相容變更可能會使更新變得非常不舒適,因此引入時應比原始語言語法中的變更更加謹慎。

對於編譯器的完全穩定版本,預設的二進位相容性協定如下:

  • 所有二進位檔都向後相容;這意味著較新的編譯器可以讀取較舊的二進位檔(例如,1.3 版可以理解 1.0 到 1.2 版)。

  • 較舊的編譯器拒絕依賴新功能的二進位檔(例如,1.0 版編譯器拒絕使用協程的二進位檔)。

  • 最好(但我們無法保證),二進位格式與下一個語言發布版本(但不是更晚的版本)大多向前相容(在新功能未使用的情況下,例如,1.9 版可以理解 2.0 版的大部分二進位檔,但不能理解 2.1 版)。

此協定旨在實現舒適的更新,因為即使專案使用的是稍舊的編譯器,也不會阻止其更新其依賴項。

請注意,並非所有目標平台都已達到此穩定性等級,但 Kotlin/JVM 已達到。

Kotlin klib 二進位檔

Kotlin klib 二進位檔在 Kotlin 1.9.20 中已達到穩定 (Stable) 等級。

但是,您需要記住一些相容性細節:

  • 自 Kotlin 1.9.20 起,klib 二進位檔向後相容。例如,2.0.x 版編譯器可以讀取由 1.9.2x 版編譯器生成的二進位檔。

  • 不保證向前相容。例如,2.0.x 版編譯器不保證能讀取由 2.1.x 版編譯器生成的二進位檔。

Kotlin cinterop klib 二進位檔仍處於 Beta 階段。

目前,我們無法為 cinterop klib 二進位檔提供不同 Kotlin 版本之間的特定相容性保證。