lexmagさん(Elixirコミッター)の The Elixir Style Guide を翻訳しました。
Elixir Style Guide
プログラマの仕事はコードを書くことよりも、むしろ問題解決の方法について別のプログラマに伝えることです。これが技術者としての成熟の最終段階です。
— What a Programmer Does, 1967
Table of Contents
コードレイアウト
-
インデントはスペース2つ。ハードタブは使わないこと。[link]
-
一行につき式は一つ。命令や式を区切るのに
;
を使ってはいけない。[link] -
- バイナリオペレータの前後、カンマ、コロン、セミコロンの後にはスペースを入れてください。
-
[]
の{}
の間にはスペースを入れないでください。 - Elixirの処理系にとって空白にほとんど意味はありませんが、コードを読みやすくするためには重要です。
[link]
sum = 1 + 2 [first | rest] = 'three' {a1, a2} = {2, 3} Enum.join(["one", <<"two">>, sum])
-
-
単項演算子やレンジリテラルの後にスペースを入れていはいけない。ただし
not
演算子だけは例外とする。
[link]angle = -45 ^result = Float.parse("42.01") 2 in 1..5 not File.exists?(path)
-
デフォルト引数
\\
の前後にスペースを入れること。[link] -
バイナリ列の範囲指定オプションにスペースを入れてはいけない。[link]
# 悪い例 <<102 :: unsigned-big-integer, rest :: binary>> <<102::unsigned - big - integer, rest::binary>> # 良い例 <<102::unsigned-big-integer, rest::binary>>
-
when
ガード節は関数、マクロ宣言と同じレベルにインデントすること。
これはwhen
ガードが関数宣言と同じ行に収まらない場合のみ適用します。 [link]def format_error({exception, stacktrace}) when is_list(stacktrace) and stacktrace != [] do # ... end defmacro dngettext(domain, msgid, msgid_plural, count) when is_binary(msgid) and is_binary(msgid_plural) do # ... end
-
複数行の式の結果を受け取るときは右辺で整列させないでください。[link]
# 悪い例 {found, not_found} = Enum.map(files, &Path.expand(&1, path)) |> Enum.partition(&File.exists?/1) prefix = case base do :binary -> "0b" :octal -> "0o" :hex -> "0x" end # 良い例 {found, not_found} = Enum.map(files, &Path.expand(&1, path)) |> Enum.partition(&File.exists?/1) prefix = case base do :binary -> "0b" :octal -> "0o" :hex -> "0x" end
-
大きい桁の整数には_
を入れて読みやすくしましょう。
[link]num = 1_000_000
-
:"foo-bar"
のようにアトムに無効な文字が含まれている場合はダブルクォートで囲うこと。
[link]# 悪い例 :'foo-bar' :'atom number #{index}' # 良い例 :"foo-bar" :"atom number #{index}"
-
行末に不要な空白を入れないこと[link]
-
ファイルの終わりには空行をいれること。[link]
-
リスト、マップ、構造体、タプルなどが各要素ごとに改行されている場合は、最後の要素の末尾にもカンマを使用することをお勧めします。[link]
[ :foo, :bar, :baz, ]
シンタックス
-
def
の引数は括弧で囲うこと。引数がない場合も省略しないこと。
[link]# 悪い例 def main arg1, arg2 do #... end def main do #... end # 良い例 def main(arg1, arg2) do #... end def main() do #... end
-
__ローカル__の関数宣言や呼び出しは引数がない場合も括弧を使うこと
[link]# 悪い例 pid = self def new, do: %MapSet{} # 良い例 pid = self() def new(), do: %MapSet{} config = IEx.Config.new
同じことがパイプライン演算子内の__ローカル__関数呼び出しにも当てはまります。
String.strip(input) |> decode()
-
関数のチェインにはパイプライン演算子
|>
を使ってください。[link]# 悪い例 String.downcase(String.strip(input)) # 良い例 input |> String.strip |> String.downcase String.strip(input) |> String.downcase
複数行のパイプラインはインデント一つで揃えます。
String.strip(input)
|> String.downcase
|> String.slice(1, 3)
無意味にパイプライン演算子を使わないこと。 [link]
# 悪い例
result = input |> String.strip
# 良い例
result = String.strip(input)
-
複数行の式の場合は、行末にバイナリ連結演算子 を書きます(唯一の例外は|>
演算子)
[link]# 悪い例 "No matching message.\n" <> "Process mailbox:\n" <> mailbox # 良い例 "No matching message.\n" <> "Process mailbox:\n" <> mailbox
-
unless
にelse
を使ってはいけない。 正常系が最初に来るように書き直してください。[link]# 悪い例 unless Enum.empty?(coll) do :ok else :error end # 良い例 if Enum.empty?(coll) do :error else :ok end
-
もしnil
を返すならif
とunless
節にelse
オプションを指定しないでください。
[link]# 悪い例 if byte_size(data) > 0, do: data, else: nil # 良い例 if byte_size(data) > 0, do: data
-
cond
構文の最後の条件は常にtrue
を使うこと。
[link]# 悪い例 cond do char in ?0..?9 -> char - ?0 char in ?A..?Z -> char - ?A + 10 :else -> char - ?a + 10 end # 良い例 cond do char in ?0..?9 -> char - ?0 char in ?A..?Z -> char - ?A + 10 true -> char - ?a + 10 end
-
厳密な真偽値チェックには||
、&&
、!
を使わないでください。いずれかの引数が真偽値でない場合にのみ、これらの演算子を使用します。
[link]# 悪い例 is_atom(name) && name != nil is_binary(task) || is_atom(task) # 良い例 is_atom(name) and name != nil is_binary(task) or is_atom(task) line && line != 0 file || "sample.exs"
-
バイナリのパターンマッチにはビット文字列構文よりもバイナリ連結演算子<>
を使うようにする。
[link]# 悪い例 <<"http://", _rest::bytes>> = input <<first::utf8, rest::bytes>> = input # 良い例 "http://" <> _rest = input <<first::utf8>> <> rest = input
-
16進数の定義には大文字を使うこと。
[link]# 悪い例 <<0xef, 0xbb, 0xbf>> # 良い例 <<0xEF, 0xBB, 0xBF>>
命名
-
アトム、関数、変数、モジュール属性にはsnake_case
を使うこと。
[link]# 悪い例 :"no match" :Error :badReturn fileName = "sample.txt" @_VERSION "0.0.1" def readFile(path) do #... end # 良い例 :no_match :error :bad_return file_name = "sample.txt" @version "0.0.1" def read_file(path) do #... end
-
モジュール名はCamelCase
を使うこと。
[link]# 悪い例 defmodule :appStack do #... end defmodule App_Stack do #... end defmodule Appstack do #... end # 良い例 defmodule AppStack do #... end
-
述語関数(ブール値を返す関数)の名前は、接頭辞has_
またはそれに類するものではなく、疑問符「?」をつけなければなりません。[link]def leap?(year) do #... end
ガード節で使う述語マクロにはis_
接頭辞をつけること。
defmacro is_date(month, day) do
#...
end
コメント
良いコードはいいジョークみたいなものだ。なんの説明もいらない。
— Russ Olsen
-
自己説明的で、コメントのいらないコードを書いてこのセクションの残りは無視してください(本気です!) [link]
-
#
とコメントの間にスペースを一つ入れること。[link] -
不要なコメントは書かない。 [link]
# 悪い例 String.first(input) # 最初の文字を取り出す.
モジュール
-
use
/import
/alias
/require
を呼び出すときは一貫した構造を用います:この順番で呼び出し、それぞれをグループにします。
[link]use GenServer import Bitwise import Kernel, except: [length: 1] alias Mix.Utils alias MapSet, as: Set require Logger
-
モジュールが自身を参照するには
__MODULE__
疑似変数を使用すること。
[link]# 悪い例 :ets.new(Kernel.LexicalTracker, [:named_table]) GenServer.start_link(Module.LocalsTracker, nil, []) # 良い例 :ets.new(__MODULE__, [:named_table]) GenServer.start_link(__MODULE__, nil, [])
正規表現
-
正規表現は最後の手段です。まずはパターンマッチングと
String
モジュールを試しましょう。
[link]# 悪い例 Regex.run(~r/#(\d{2})(\d{2})(\d{2})/, color) Regex.match?(~r/(email|password)/, input) # 良い例 <<?#, p1::2-bytes, p2::2-bytes, p3::2-bytes>> = color String.contains?(input, ["email", "password"])
-
キャプチャの結果が必要がないならキャプチャしないグループを使うこと。
[link]~r/(?:post|zip )code: (\d+)/
-
^
と$
は行の開始と終了にそれぞれマッチするので注意してください。 全ての文字列にマッチさせたい場合は、\ A
と\ z
(\ n?\ z
と等価な\Z
と混同しないでください)を使います。
[link]
例外
-
例外の名前は
Error
で終わるようにすること。
[link]# 悪い例 ResponseException # 良い例 ResponseError
-
エラーメッセージは全て小文字で句読点を省いてください
[link]# 悪い例 raise ArgumentError, "Malformed payload." # 良い例 raise ArgumentError, "malformed payload"
例外として、Mixのエラーメッセージだけは常に大文字から始めます。
Mix.raise "Could not find dependency"
License
This work was created by Aleksei Magusev and is licensed under the CC BY 4.0 license.
Credits
The structure of the guide and some points that are applicable to Elixir were taken from the community-driven Ruby coding style guide.