此 EEP 提議一個官方 API 文件儲存方式,供 BEAM 語言使用。透過標準化 API 文件儲存方式,將可以開發出適用於各種語言的工具。
目前,在 BEAM 上執行的不同程式語言和函式庫,都有各自的架構來儲存和存取文件。
例如,Elixir 和 LFE 在其 shell 中提供了 h
輔助工具,可以印出任何模組的文件。
iex> h String
A String in Elixir is a UTF-8 encoded binary.
然而,Elixir 只能顯示 Elixir 模組的文件。LFE 只能顯示 LFE 函式的文件,以此類推。如果文件可以標準化,就可以輕鬆地將這些功能新增到其他語言,並以一致的方式在所有 BEAM 語言中運作。
此外,每種語言最終都會建立自己的工具來產生、處理和轉換文件。我們希望統一的文件方法能夠改善工具之間的相容性。例如,Erlang IDE 將能夠顯示任何模組和函式的內嵌文件,無論該函式是 OTP 的一部分、函式庫,甚至是使用 Elixir、LFE 或 Alpaca 編寫的。
注意:在此文件中,「文件」一詞僅指模組和函式的 API 文件。指南、教學和其他材料對於專案也很重要,但不是此 EEP 的重點。
注意:此 EEP 不是關於文件格式,而是關於儲存文件的機制,以便更容易產生其他格式。例如,工具可以讀取文件並從中產生 man 頁面。
此 EEP 分為三個部分。第一部分定義了文件可以儲存的兩個位置,第二部分定義了文件的形狀,第三部分討論了與 OTP 的整合。
BEAM 語言儲存文件主要有兩種機制:在檔案系統中(通常在 /doc
目錄中)和在 .beam
檔案內。
此 EEP 認可這兩種選項並旨在支援它們。要尋找名為 example
的模組的文件,工具應該
在程式碼路徑中尋找 example.beam
,解析 BEAM 檔案並擷取 Docs
區塊
如果沒有區塊,則應該在程式碼路徑中尋找 "example.beam" ,並在定義 example
模組的應用程式中尋找 doc/chunks/example.chunk
檔案
如果沒有 .chunk
檔案,則沒有文件
使用區塊或檔案系統的選擇完全取決於語言或函式庫。在這兩種情況下,都可以隨時透過移除 Docs
區塊或移除 doc/chunks
目錄來新增或移除文件。
例如,像 Elixir 和 LFE 這樣的語言會在編譯時附加 Docs
區塊,這可以透過編譯器標誌控制。另一方面,像 OTP 這樣的專案可能會在與程式碼編譯完全無關的單獨命令中產生 doc/chunks
條目。
在兩種儲存方式中,文件都以完全相同的格式編寫:一個 Erlang 項,透過 term_to_binary/1
序列化為二進制檔案。該項在序列化時可以選擇性地壓縮,並且必須遵循以下的類型規格
{docs_v1,
Anno :: erl_anno:anno(),
BeamLanguage :: atom(),
Format :: mime_type(),
ModuleDoc :: #{optional(DocLanguage) := DocValue} | none | hidden,
Metadata :: map(),
Docs ::
[{{Kind, Name, Arity},
Anno :: erl_anno:anno(),
Signature :: [binary()],
Doc :: #{optional(DocLanguage) := DocValue} | none | hidden,
Metadata :: map()
}]} when DocLanguage :: binary(),
DocValue :: binary() | term()
在根元組中,我們有
Anno
- 定義本身的註解(行、欄、檔案)(請參閱 erl_anno
)
BeamLanguage
- 代表語言的原子,例如:erlang
、elixir
、lfe
、alpaca
等
Format
- 文件的 MIME 類型,例如 "text/markdown" 或 "application/erlang+html"(請參閱 FAQ 中關於此欄位的討論)
ModuleDoc
- 以文件語言作為鍵值的 map,例如 <<"en">>
或 <<"pt_BR">>
,並以二進制值作為文件。如果沒有文件,則可以是原子 none
,如果已明確停用此條目的文件,則可以是原子 hidden
Metadata
- 以原子鍵值為鍵的 map,且以任何項為值。這可以用來新增註解,例如模組的「作者」、「已棄用」或語言或文件工具可能認為相關的任何其他內容
Docs
- 模組中其他實體(例如函式和類型)的文件清單
在 Docs
中的每個條目,我們都有
{Kind, Name, Arity}
- 識別函式、回呼、類型等的種類、名稱和元數。官方的實體是:function
、type
和 callback
。其他語言會新增它們自己的實體。例如,Elixir 和 LFE 可以新增 macro
Anno
- 模組文件或定義本身的註解(行、欄、檔案)(請參閱 erl_anno)
Signature
- 實體的簽名。它是二進制檔案的清單。每個條目表示簽名中可以與空格或換行符連接的二進制檔案。例如,["binary_to_atom(Binary, Encoding)", "when is_binary(Binary)"]
可以呈現為單行或兩行。它的存在僅供展示之用
Doc
- 以文件語言作為鍵值的 map,例如 <<"en">>
或 <<"pt_BR">>
,並以值作為文件。文件可以是二進制檔案或任何 Erlang 項,兩者都由 Format
描述。如果是 Erlang 項,則 Format
必須是 "application/erlang+SUFFIX",例如當文件是 HTML 文件的 Erlang 表示時,則為 "application/erlang+html"。如果沒有文件,Doc
也可以是原子 none
,如果已明確停用此條目的文件,則可以是原子 hidden
Metadata
- 以原子鍵值為鍵的 map,且以任何項為值
注意:文件 map 可以是空的。在這種情況下,已將對該函式的參考新增到文件索引中,使其有效地公開,但沒有編寫任何文件。
這種共用格式是 EEP 的核心,因為它可以有效地實現跨語言協作。
Metadata
欄位的存在是為了讓語言、工具和函式庫可以將自訂資訊新增到每個條目。此 EEP 記錄了以下中繼資料鍵值
authors := [binary()]
- 以二進制檔案形式顯示的作者清單
cross_references := [module() | {module(), {Kind, Name, Arity}}]
- 模組或模組條目的清單,可以用於產生文件時的交叉參考
deprecated := binary()
- 如果存在,表示目前的條目已棄用,並附上表示棄用原因的二進制檔案以及取代已棄用程式碼的建議
since := binary()
- 表示新增此條目的版本的二進制檔案,例如 <<"1.3.0">>
或 <<"20.0">>
edit_url := binary()
- 表示變更文件本身的 URL 的二進制檔案
可以隨時將任何鍵值新增到 Metadata。社群經常使用的鍵值可以在未來版本中標準化。
最後一部分重點在於將前幾個部分與 OTP 文件、工具和工作流程整合。以下項目是建議,對於 OTP 或任何其他語言或函式庫採用此 EEP 並非必要。
此時,我們應該考慮對 OTP 進行變更,例如
將 doc/chunks/*.chunk
檔案作為 OTP 的一部分分發,並變更與 OTP 一起提供的工具以依賴它們。例如,可以變更 erl -man lists
來定位 lists.chunk
檔案、解析文件,然後即時將其轉換為 man 頁面。這項工作可能需要多項變更,因為 OTP 將文件儲存在 XML 檔案以及原始碼中。edoc
本身應該使用可以從原始碼中輸出 .chunk
檔案的函式來擴充
將 h(Module)
、h(Module, Function, Arity)
和類似的函式新增到 Erlang 的 shell 中,以印出模組或給定函式和元數的文件。這應該能夠印出任何其他實作此提案的函式庫或語言的文件
問:為什麼文件中會有 Format 條目?
此提案中的主要權衡是文件格式。我們有兩個選項
統一的文件格式讓語言和函式庫在選擇如何編寫文件方面沒有彈性。隨著生態系統變得更加多樣化,不太可能找到適合所有人的格式。因此,我們引入了 Format 欄位,允許每種語言和函式庫選擇其文件格式。缺點是,如果 Elixir 文件以 Markdown 編寫,而某種語言不知道如何格式化 Markdown,那麼該語言將必須選擇不顯示 Elixir 文件或以原始格式(即 Markdown)顯示它們。
Erlang 處於有利位置。所有語言都將能夠支援為 Erlang 選擇的任何格式,因為所有語言都在 Erlang 上執行,並且可以直接存取 Erlang 的工具。
問:如果我有一個使用自訂文件工具組的 Erlang/Elixir/LFE/Alpaca 函式庫,我也可以利用這個功能嗎?
只要文件最終出現在 Docs
區塊或 doc/chunks
目錄內,我們完全不關心文件最初是如何撰寫的。如果您使用自訂格式,您可能需要教導您選擇的語言如何呈現它。請參閱先前的問題。
本文件已置於公共領域。