作者
José Valim <jose(dot)valim(at)gmail(dot)com>
狀態
最終版/27.0 已在 OTP 版本 27 中實作
類型
標準追蹤
建立於
2021-06-02
發布歷史
https://github.com/erlang/otp/pull/7936 https://github.com/erlang/otp/pull/8165 https://github.com/erlang/otp/pull/8127 https://github.com/erlang/otp/pull/8123 https://github.com/erlang/otp/pull/8102 https://github.com/erlang/otp/pull/8089 https://github.com/erlang/otp/pull/8063 https://github.com/erlang/otp/pull/8026 https://github.com/erlang/otp/pull/7999 https://github.com/erlang/otp/pull/7959 https://github.com/erlang/otp/pull/7958

EEP 59: 模組屬性用於文件 #

摘要 #

此 EEP 提出一個針對 Erlang 的結構化文件 API,其中文件將作為語言解析器的一部分處理,並直接包含在已編譯的 .beam 檔案中,以取代 EDoc 風格的註解。Python、Elixir 和 Clojure 是遵循此方法將文件視為資料而非程式碼註解的語言範例。

原理 #

目前 EDoc 的主要限制在於文件以程式碼註解的形式保存。這需要一個明確的工具來解析這些程式碼註解,這使得 IDE、shell 等等對文件的存取變得複雜。最近在這方面有所改進,透過使 EDoc 編譯為 EEP 48,但仍然需要一個明確的步驟。

此外,就實作而言,「程式碼註解」的方法更加複雜,因為它需要解析原始程式碼以及程式碼註解、解析程式碼註解等等。明確區分文件和程式碼註解也很有益:它們有不同的要求和不同的受眾。

此 EEP 建議為 Erlang 新增兩個模組屬性:-doc-moduledoc

如同 EEP 48,此提案僅與 API 參考及其文件相關。它不涵蓋指南、教程和其他文件格式。

新的模組屬性 #

此 EEP 建議兩個新的屬性:-doc-moduledoc。它們可以按如下方式使用

-module(base64).
-moduledoc "
Convenience functions for encoding and decoding from base64.
".

-doc "
Encodes the given binary to base64.
".
-spec encode(binary()) -> binary().
encode(Binary) ->
  % ....

-doc "
Decodes the given binary from base64.
".
-spec decode(binary()) -> {ok, binary()} | error.
encode(Binary) ->
  % ....

新的 -moduledoc 屬性可以列在任何位置,它將包含給定模組的文件。-doc 屬性必須列在函式、類型屬性或回呼屬性之前的任何位置,並且它將包含以下函式、類型或回呼的文件。例如,下面的例子

-doc "Example".
-spec example() -> ok.
example() -> ok.

等同於

-spec example() -> ok.
-doc "Example".
example() -> ok.

為同一個函式列出多個帶有字串值的 -doc 屬性應相應地發出警告或錯誤,除非文件被設定為隱藏。例如,這是有效的

-doc "Example".
-doc hidden.
example() -> ok.

但這應該警告/錯誤

-doc "Example".
-doc "Updated example".
example() -> ok.

這也一樣

-doc "Example".
example(one) -> 1;
-doc "Updated example".
example(two) -> 2;

隱藏文件 #

模組屬性必須是字串或原子 false。將模組標記為隱藏表示它不會成為文件的一部分。例如,假設上面的 base64 模組將其某些邏輯委派給私有的 base64_impl 模組

-module(base64_impl).
-moduledoc false.

請注意,模組可能會被隱藏,但個別函式仍然可以被記錄

-module(base64_impl).
-moduledoc false.

-doc "
Some comments as if it was public.
".
decode64(Binary) ->
  % ...

根據 EEP 48,這是故意的。例如,base64_impl 對於 base64 功能的使用者來說應該是私有的,但直接在 base64 上工作的開發人員可能仍然希望直接從他們的 IDE 存取 base64_impl 函式的文件。每個文件工具都應相應地處理 hidden。如果沒有提供 -doc,根據 EEP 48,它預設為 none

-doc 屬性也接受 false 原子。

檔案/包含文件 #

有些開發人員不喜歡將文件與原始程式碼放在一起。對於這種情況,-doc-moduledoc 也可以提供 {file, Path},其中 Path 是包含 doc 屬性的檔案的相對路徑

-moduledoc({file, "../doc/src/manual/my_module.asciidoc"}).
-doc({file, "../doc/src/manual/my_module.my_function.asciidoc"}).

編譯器將讀取該檔案並在編譯時將其嵌入到區塊中。

回呼和類型 #

-doc 屬性也可以用於記錄類型和回呼。

私有函式 #

-doc 屬性也可以用於私有函式,以便工具和 IDE 可以在使用者需要時提供文件。但是,私有函式不應出現在 EEP 48 doc 區塊中。

私有類型 #

一般而言,私有類型的處理方式與私有函式相同,但是有時類型用於模組內的文件和程式碼共享目的,但使用者不希望將其匯出以供一般使用。一個例子是 ssl 模組中的所有各種選項類型。

因此,任何由公共函式規範或類型引用的私有類型也將包含在文件區塊中。此類類型的 exported 元資料鍵將設定為 false

元資料 #

新的模組屬性還必須支援文件元資料,方法是傳遞一個映射作為引數

-module(beam64).
-moduledoc "
Convenience functions for encoding and decoding from base64.
".
-moduledoc #{
  author => [<<"The Erlang/OTP team">>],
  license => <<"Apache 2 License">>,
  cross_references => [binary]
}.

如果多次使用映射呼叫 -moduledoc,則映射將被合併。這帶來了額外的好處,即共用的元資料可以移至標頭檔

%% prelude.hrl
-moduledoc #{
  authors => [<<"The Erlang/OTP team">>],
  license => <<"Apache 2 License">>
}.

然後我們可以包含並擴充它

-module(beam64).
-include("prelude.hrl").
-moduledoc "
Convenience functions for encoding and decoding from base64.
".
-moduledoc #{cross_references => [binary]}.

內建屬性列表可在 EEP 48 上找到。

編譯 #

編譯具有 -moduledoc-doc 屬性的模組將在其 .beam 檔案中產生一個 Docs 區塊,使文件可以直接在 shell 中存取。

發布工具也應預設從 .beam 檔案中刪除文件區塊。請注意,這已經由 beam_lib:strip_release/1beam_lib:strip_files/1 完成。

warn_missing_doc #

如果將 warn_missing_doc 標誌傳遞給編譯器,則編譯器會在 EEP 48 doc 區塊中包含的函式、類型或回呼未設定 -doc 屬性時發出警告。

該標誌可以在命令列上作為 +warn_missing_doc 傳遞,也可以作為原始程式碼中的 -compile(warn_missing_doc) 屬性傳遞。

文件格式 #

關於文件的一個重要討論是文件應採用的文件格式是什麼。幸運的是,EEP 48 不受格式限制,但是仍然必須列出一個格式。

為了方便使用多種文件格式,Erlang/OTP 允許使用者在 -moduledoc 元資料中放置一個 format 鍵,該鍵應指定所用格式的 mime 類型,如 EEP 48 所指定。預設格式將為 text/markdown

-moduledoc(#{ format => "text/edoc" }).

其他主題 #

Doctests #

使文件更結構化且可存取的直接後果是,Erlang 可以包含 doctests,這是執行和驗證文件中範例的能力。例如,有人可以這樣寫

-doc """
Encodes the given binary to base64.

1> base64:encode("hello").
<<"aGVsbG8=">>

""".
-spec encode(binary()) -> binary().
encode(Binary) ->
  % ....

然後在你的測試套件中

doctests(_Config) ->
  ct_doctest:run(base64).

doctest 屬性將存取 base64 Docs 區塊中的文件條目,提取所有範例並執行它們。當然,儘管沒有任何東西阻止在今天的 EDoc 之上實作 doctests,但此 EEP 使 doctests 的實作變得簡單得多。

Doctests 將受益於單獨的 EEP,因為還有一些額外的考慮,例如 doctesting 例外、無法解析的格式等等,但鑑於它們對使用者和文件作者的好處,值得一提。

EDoc 和 erl_docgen 呢? #

如果此提案被接受,EDoc 會發生什麼事?

此 EEP 提出的工作的一個重要方面是嘗試統一 Erlang/OTP 生態系統中的文件工具。在此之前,有多種工具,EDoc 主要用於開源專案,erl_docgen 用於 Erlang/OTP 專案和其他第三方解決方案。

在短期內,EDoc 將被更新為能夠從包含 text/edoc 文件的 EEP 48 doc 區塊生成 html 報告。指定文件的 EDoc 註解風格將被棄用,但是,為了向後相容性,解析 EDoc 註解的支援將保留很長時間。

從長遠來看,目標是切換文件渲染引擎以使用 ExDoc,它透過將其作為 escript 執行或透過 Rebar3 整合來支援 Erlang 專案。使用 ExDoc 時,使用者可以選擇使用 EDoc 語法或遷移到 Markdown。

同樣,Erlang/OTP 程式碼將被轉換為使用 ExDoc 而不是 erl_docgen 來產生文件。所有目前的 XML 文件檔案都將被轉換為使用 Markdown。

版權 #

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