在 Elixir 裡,文件是一等公民。
有兩個模組屬性經常用來為程式碼撰寫文件:@moduledoc用來記錄模組的文件,@doc則用來記錄緊接在該屬性後面的函式。@moduledoc屬性通常會出現在模組的第一行,而@doc屬性通常會出現在函式定義的正前方,如果函式有型別規格的話,就放在型別規格前面。文件通常使用 heredoc 語法寫成多行字串。
Elixir 的文件是以 Markdown 撰寫的。
defmodule String do
@moduledoc """
Strings in Elixir are UTF-8 encoded binaries.
"""
@doc """
Converts all characters in the given string to uppercase according to `mode`.
## Examples
iex> String.upcase("abcd")
"ABCD"
iex> String.upcase("olá")
"OLÁ"
"""
def upcase(string, mode \\ :default)
end
Elixir 是動態型別的語言,這表示它不提供編譯期的型別檢查。不過,型別規格還是可以當作一種文件形式來使用。
你可以使用@spec模組屬性,把型別規格加在函式定義的正前方。@spec後面接著函式名稱,以及一組放在括號裡、以逗號分隔的所有引數型別。回傳值的型別和函式的引數之間,用雙冒號::分隔。
@spec longer_than?(String.t(), non_neg_integer()) :: boolean()
def longer_than?(string, length), do: String.length(string) > length
最常用的型別有:
boolean()
String.t()
integer()、non_neg_integer()、pos_integer()、float()
list()
any()
有些型別還能參數化,例如list(integer)就是一個整數的陣列。
字面值也可以當作型別使用。
型別的聯集可以用垂直線|來表示。例如integer() | :error的意思是:它可能是整數,也可能是原子字面值:error。
完整的型別清單可以在官方文件的「Typespecs」一節中找到。
型別規格裡的引數也可以命名,這在區分多個同型別的引數時很有用。引數名稱後面接著雙冒號,放在該引數的型別前面。
@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()
型別規格並不限於內建型別而已。你可以使用@type模組屬性來定義自訂型別。自訂型別的定義以型別名稱開頭,後面接著雙冒號,然後才是型別本身。
@type color :: {hue :: integer, saturation :: integer, lightness :: integer}
@spec to_hex(color()) :: String.t()
自訂型別可以在定義它的同一個模組裡使用,也可以從其他模組使用。
你已經在市公所工作了好一陣子,也開發出一套能加快日常工作的工具,例如填寫表單。
現在有位新同事要加入你們,而你發現自己的工具可能不夠淺顯易懂。你們辦公室裡有許多奇怪的慣例,例如表單一律用大寫字母填寫,還有避免讓欄位留空。
你決定撰寫一些文件,讓新同事能更快上手,直接開始使用你的工具。
為 Form 模組撰寫說明其用途的文件。內容應該是:
A collection of loosely related functions helpful for filling out various forms at the city office.
為 Form.blanks/1 函式撰寫文件和型別規格。文件內容應該是:
Generates a string of a given length.
This string can be used to fill out a form field that is supposed to have no value.
Such fields cannot be left empty because a malicious third party could fill them out with false data.
型別規格應該說明這個函式接受一個引數(非負整數),並回傳一個字串。
為 Form.letters/1 函式撰寫文件和型別規格。文件內容應該是:
Splits the string into a list of uppercase letters.
This is needed for form fields that don't offer a single input for the whole string,
but instead require splitting the string into a predefined number of single-letter inputs.
型別規格應該說明這個函式接受一個引數(字串),並回傳一個由字串組成的陣列。
為 Form.check_length/2 函式撰寫文件和型別規格。文件內容應該是:
Checks if the value has no more than the maximum allowed number of letters.
This is needed to check that the values of fields do not exceed the maximum allowed length.
It also tells you by how much the value exceeds the maximum.
型別規格應該說明這個函式接受兩個引數,一個字串和一個非負整數,並回傳兩種可能的值之一。它會回傳 :ok 原子,或是回傳一個二元組,其中第一個元素是 :error 原子,第二個是正整數。
不知道是什麼原因,市公所的內部系統用了兩種不同的方式來表示地址:一種是映射,另一種是元組。
用定義三個自訂的公開型別來記錄這件事:
address_map:帶有 :street、:postal_code 和 :city 鍵的映射。每個鍵都存放一個字串型別的值。address_tuple:帶有三個值的元組:street、postal_code 和 city。每個值的型別都是字串。在型別規格中為這些值命名,以區分它們。address:可以是 address_map,也可以是 address_tuple。為 Form.format_address/1 函式撰寫文件和型別規格。文件內容應該是:
Formats the address as an uppercase multiline string.
型別規格應該說明這個函式接受一個引數(地址),並回傳一個字串。