作者
Ilya Klyuchnikov <ilya(點)klyuchnikov(在)gmail(點)com>
狀態
草稿
類型
標準追蹤
建立日期
2023年9月1日

EEP 65: import_type 指令 #

摘要 #

此 EEP 提議一個新的 -import_type(Module, [Types]) 模組指令來匯入遠端類型。匯入的類型可以像本地類型一樣被引用,無需使用模組前綴。

原理和動機 #

此提案背後的理由與現有的 -import(Module, [Functions]) 指令一致。引入此指令的主要動機如下:

  • 簡化大量使用遠端類型的模組。在模組頻繁引用特定遠端類型的情境中,使用其完整限定名稱可能會變得繁瑣。例如,erl_lint 模組定義了一個 本地類型 anno() 來引用類型 erl_anno:anno()
  • 減少普遍存在類型的冗長度。在某些應用程式或專案中,某些類型可能非常常用,以至於用完整名稱引用它們會變得過於冗長。常見的解決方法是將這些類型放在標頭檔中,然後將此標頭檔包含在應用程式的大多數模組中。例如:dialyzer.hrl 標頭檔中的常見類型。這種方法的缺點是,它會為工具增加額外的負擔(例如 Dialyzer 和類型檢查器)。每個模組最終都會擁有其自己的類型本地副本,這可能導致重複比較同一類型的不同版本,並減慢分析速度。

它使得匯出和匯入類型對稱於匯出和匯入函數。到目前為止,存在不對稱性:類型可以匯出,但不能匯入。

詳細資訊 #

引入新的模組指令

-import_type(Module, [T1/A1, ..., Tk/Ak]).

這裡,Module 是一個原子(匯入類型的模組名稱),Ti's 是原子(類型名稱),而 Ai's 是整數(arity)。匯入的類型可以像本地類型一樣被引用 - 無需任何模組前綴。

範例

-module(m1).
-import_type(common_types, [user/0, id/0]).

-spec get_user_id(user()) -> id().

對類型的引用應該明確地解析為本地定義的類型、預定義的內建類型或匯入的類型。這會施加一些限制(類似於匯入函數的限制)

  1. 匯入指令不能覆蓋內建類型。
  2. 一個類型 Ti/Ai 只能被匯入一次。
  3. 匯入的類型不能在模組中本地重新定義。

這些限制由編譯器驗證(作為 erl_lint 階段的一部分)。工具負責檢查匯入的遠端類型是否存在和是否已匯出,編譯器不檢查它。

限制範例

覆蓋內建類型

-module(m2).
-import_type(m1, [binary/0]).

error: import directive overrides auto-imported builtin type binary/0

匯入兩次

-module(m).
-import_type(m1, [user/0]).
-import_type(m2, [user/0]).

error: type user/0 already imported from m1.

從同一個模組匯入兩次

-module(m).
-import_type(m1, [a/0, b/0]).
-import_type(m1, [b/0, c/0]).

error: type b/0 already imported from m1

重新定義匯入的類型

-module(m).
-import_type(m1, [user/0]).

-type user() :: {user, binary()}.

error: defining imported type user/0

參考實作 #

https://github.com/erlang/otp/pull/7618

此實作包含三個邏輯部分

  • 解析器(erl_parse)中的變更以支援新指令。
  • erl_lint 中的變更以強制執行限制(來自詳細資訊章節)
  • 匯入類型的擴展由處理函數匯入的相同編譯器傳遞處理。因此,匯入的函數和匯入的類型以相同的方式處理。Dialyzer 已經獲得了擴展的匯入類型,因此它「就可用了」。

向後相容性 #

此變更是向後相容的。現有程式碼不能有提議的 import_type 屬性,因為目前的編譯器無法解析它們。

版權 #

本文件置於公共領域或在 CC0-1.0-Universal 許可證下,以較寬鬆者為準。