Elixirでは、ドキュメントは第一級の存在として扱われます。
コードにドキュメントを付けるためによく使われるモジュール属性が2つあります。モジュールを記述する@moduledocと、その属性の直後にある関数を記述する@docです。@moduledoc属性はたいていモジュールの最初の行に置き、@doc属性はたいてい関数定義の直前、型仕様があればその直前に置きます。ドキュメントは、ヒアドキュメント構文を使った複数行の文字列として書くのが一般的です。
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の後には、関数名と、そのすべての引数の型のリストを括弧(())で囲んで書きます。型はカンマで区切ります。戻り値の型は、関数の引数とコロン2つ::で区切ります。
@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のどちらかを意味します。
すべての型の一覧は、公式ドキュメントの「型仕様」のセクションにあります。
型仕様の引数には名前を付けることもできます。同じ型の引数が複数あるときに区別できて便利です。引数名の後にコロン2つを書き、その後に引数の型を書きます。
@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()
型仕様は組み込みの型だけに限りません。カスタム型は@typeモジュール属性を使って定義できます。カスタム型の定義は、型の名前から始まり、その後にコロン2つ、そして型そのものを書きます。
@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.
型仕様では、この関数が2つの引数(文字列と非負の整数)を受け取り、2通りの値のうちどちらかを返すことを説明します。返すのは、:okアトムか、1つ目の要素が:errorアトムで2つ目が正の整数である2要素のタプルです。
理由は不明ですが、市役所の内部システムでは、住所を表す方法が2つあります。マップか、タプルです。
3つのカスタム公開型を定義して、このことをドキュメント化します。
address_mapは、キー:street、:postal_code、:cityを持つマップです。それぞれのキーの値は文字列型です。address_tupleは、3つの値street、postal_code、cityを持つタプルです。それぞれの値は文字列型です。型仕様では、それぞれの値に名前を付けて区別します。addressは、address_mapかaddress_tupleのどちらかです。Form.format_address/1関数に、ドキュメントと型仕様を追加します。ドキュメントの内容は次のとおりです。
Formats the address as an uppercase multiline string.
型仕様では、この関数が1つの引数(住所)を受け取り、文字列を返すことを説明します。