みずりゅの自由帳

主に参加したイベントやソフトウェア技術/開発について記録しています

ネタ:Elixirへのいざない外伝:ラクダとヘビ

注:薄い本(技術同人誌)向けのネタ話です。

以前、こんなネタを書きました。今回も似たようなものを。

mzryuka.hatenablog.jp

今回は"命名規則"について。


第2話:ラクダとヘビ

Elixir使いの少女エリィは、いつものように総合格闘プログラミング*1の練習をしていた。

エリィ「ふう、ちょっと休憩しよう」

休憩中、エリィはラクダとヘビが言い争っているシーンを目撃する。

ラクダ「僕の方が、単語の区切りが明確になっているし、短くかける」
ヘビ「いやいや、単語の区切りなら僕の方がより明確だ。それに長くはなるけど、スペースの置換と考えれば元の英文に近い」

どうやら、どちらも自分が優れていると主張しているようです。
ことの成り行きをハラハラしながら見ているエリィ。
そんなエリィに気づいた2匹。

ラクダ/ヘビ「「ねぇ、君はどっちが優れていると思う!!」」
エリィ「え!? わたし!!」

どうやら、2匹の争いごとにエリィは巻き込まれてしまったようです。

エリィ「(うーん、どうしよう。ラクダさんの主張もヘビさんの主張もどっち正しいような気もするし。でも、よく考えると、どっちでも良いきがするし。でもでも、統一されていないと、チーム戦では困る気もするし...)」

ラクダとヘビの熱い視線がエリィに向けられます。

エリィ「そうだ! 困った時には本家サイト様のドキュメント!!」

そう言うと、エリィは魔法のタブレットを取り出して、Elixirの本家サイトのドキュメントを呼び出すのでした。


エリィ「ふむふむ。本家サイト様の書いたドキュメントによると、変数名/関数名/モジュールの属性名はヘビさんタイプにすべしと書いてあるわ」

ヘビ「やった!」
ラクダ「ぐぬぬ

エリィ「でも、すべしと書いてあるけど、別にラクダさんタイプでもエラーは起こらないみたいね。あと、どちらにしろ、最初の文字が大文字なのはダメみたい」

iex(1)> foo_bar = 10
10
iex(2)> fooBar = 20
20
iex(3)> Foo_bar = 30
** (MatchError) no match of right hand side value: 30

iex(3)> FooBar = 40
** (MatchError) no match of right hand side value: 40

iex(3)>
iex(3)> defmodule MyFooBar do
...(3)>   def foo_bar(x), do: x
...(3)>   def fooBar(x), do: x
...(3)>   def FooBar(x), do: x
** (SyntaxError) iex:6: unexpected ( after alias FooBar. Function names and identifiers in Elixir start with lowercase characters or underscore. For example:

ラクダ「ほっ。アッパーキャメルケース*2でなければ、僕でもいいんだね。」
ヘビ「...でも、僕の方が推奨されてるのは間違いないんだ」


エリィ「...あ! エイリアス名/モジュール名についてはラクダさんタイプとすると書いてある!!」

ラクダ「やった!」
ヘビ「ぐぬぬ

エリィ「こちらについては、ヘビさんタイプにしようとするとエラーが起こるみたいね。それと、ラクダさんタイプでも先頭文字は大文字にしないとダメみたい

iex(1)> defmodule my_hoge do
...(1)>   def is_hoge(:hoge), do: :ok
...(1)>   def is_hoge(_), do: :error
...(1)> end
** (CompileError) iex:1: undefined function my_hoge/0
    (stdlib 3.12.1) lists.erl:1354: :lists.mapfoldl/3
    (elixir 1.10.3) expanding macro: Kernel.defmodule/2
    iex:1: (file)
iex(1)> defmodule myHoge do
...(1)>   def is_hoge(:hoge), do: :ok
...(1)>
...(1)>   def is_hoge(_), do: :error
...(1)> end
** (CompileError) iex:1: undefined function myHoge/0
    (stdlib 3.12.1) lists.erl:1354: :lists.mapfoldl/3
    (elixir 1.10.3) expanding macro: Kernel.defmodule/2
    iex:1: (file)
iex(1)> defmodule MyHoge do
...(1)>   def is_hoge(:hoge), do: :ok
...(1)>   def is_hoge(_), do: :error
...(1)> end
{:module, MyHoge,
 <<70, 79, 82, 49, 0, 0, 4, 228, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 142,
   0, 0, 0, 16, 13, 69, 108, 105, 120, 105, 114, 46, 77, 121, 72, 111, 103, 101,
   8, 95, 95, 105, 110, 102, 111, 95, 95, ...>>, {:is_hoge, 1}}
iex(2)>

ラクダ「ふふん。エラーになるってことは、それだけ優先度が高いってことだ。つまり僕の方が優勢ってことさ」
ヘビ「ぐっ。でも、今はひとまず1勝1敗だろ。きみぃ! 次をお願い!」


エリィ「...うーん。あ! Atomについてはどっちでもいいって書いてある」

ラクダ/ヘビ「「あんですと!?」」

iex(1)> :this_is_a_pen
:this_is_a_pen
iex(2)> :thisIsAPen
:thisIsAPen
iex(3)>

エリィ「でも、慣例ではヘビさんタイプしとけとも書いてある」

ヘビ「やった!」
ラクダ「ぐぬぬ。か、慣例なら僕にだってチャンスがある」


エリィ「...これが最後かな? ファイル名は慣例としてヘビさんタイプにするだって」

ヘビ「やった!」
ラクダ「ぐぬぬ。でも、今回だって慣例なんだろ?」

エリィ「そうね。慣例なので、別にラクダさんタイプにしてもエラーは発生しないみたい」*3

$cat my_hoge.ex
defmodule MyHoge do
  def is_hoge(:hoge), do: :ok
  def is_hoge(_), do: :error
end
$
$cat myHoge.ex
defmodule MyHoge do
  def isHoge(:hoge), do: :ok
  def isHoge(_), do: :error
end
$
$iex
Erlang/OTP 22 [erts-10.7.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> c("my_hoge.ex")
[MyHoge]
iex(2)> MyHoge.is_hoge(:hoge)
:ok

iex(3)> c("myHoge.ex")
warning: redefining module MyHoge (current version defined in memory)
  myHoge.ex:1

[MyHoge]
iex(4)> MyHoge.is_hoge(:hoge)
** (UndefinedFunctionError) function MyHoge.is_hoge/1 is undefined or private. Did you mean one of:

      * isHoge/1

    MyHoge.is_hoge(:hoge)
iex(4)> MyHoge.isHoge(:hoge)
:ok
iex(5)>


エリィ「あ! でも、ファイル名の慣例は"定義したモジュール名をヘビさんタイプにする"って書いてあるよ」

ラクダ「僕の名前をベースにして...」
ヘビ「僕の名前をつける...」

エリィ「そうだよ! それに、わたしの使うElixirでは、ヘビさんもラクダさんもどちらも大事なんだよ」

ラクダ「どっちも...」
ヘビ「だいじ...」

エリィ「そうじゃなきゃ、どっちか片方に寄せていると思うの。どちらも必要だから、どちらのタイプについても取り扱っているんだよ!」

お互いを見合っているラクダとヘビ。
納得いかないような、それでいて、どちらも嬉しそうな、そんな複雑な表情をしている。

エリィ「それに! あなた達に聞かれなかったら、わたしきっと適当に名前をつけていたと思うの。だから、わたしにとってもあなた達は...」


感謝の言葉を投げかけようとしたエリィであったが、ラクダとヘビは出会った時と同じような口論しながらその場を離れていった。

エリィ「(もしかしたら、あの方達はああやって総合格闘プログラミングの方々に助言を与えているのかしら?)」

2匹の背中を見送りながら、昼食はケバブ*4を食べてみようかな、と思うエリィなのでした。

第2話 終わり

元ネタ

最初に書いていますが、命名規則についてです。

Elixirの命名規則については以下のようになっています。

  • 変数名/関数名/モジュールの属性名:スネークケース*5
  • モジュール名/エイリアス名:キャメルケース(先頭大文字)
  • Atom:慣例としてスネークケース(キャメルケースでも可)
  • ファイル名:慣例としては、"定義したモジュールをスネークケースにしたもの"で名前をつけます。例えば、モジュール名がMyAppの場合は、ファイル名はmy_app.exとつける。*6

詳細はこちら。

hexdocs.pm

なお、コマンドについては、Elixir 1.10で確認しています。

書こうと思ったきっかけは、こちらのツイート。


自著内で、関数名はスネークケースで書くのを意識していたのに、キャメルケースで書いてあった箇所もあったので自戒の意味も込めて。*7

*1:考えるな。感じろ。

*2:もしくはパスカルケース

*3:例では、「my_hoge.ex」と「myHoge.ex」をiex上で読み出しています。モジュール名が同じため、後から呼び出したモジュールの定義で関数名が再設定されています。

*4:ケバブケースは、単語の間をハイフンでつなぐ命名規則。チェーンケースとも言う

*5:hexdocsにはElixirデベロッパーはmustと書いてありますが、キャメルケースで書いてもエラーとまではなりません。ただし、先頭大文字はNG

*6:ただし、これも慣例らしいので、ファイル名はどうとでも作れてしまう

*7:エンジニアなら、自動チェックするツール使えよ、と言うツッコミはあるでしょうが...