<Enjoy-Elixir>拡張子ex,exs

目次

Enjoy-Elixirと称する、プログラミング言語Elixirを勉強する記事群の1つです。

Elixirコードファイルの拡張子

Elixir のコードファイルに使う拡張子は慣習的に次の2つです。

  • ex
  • exs

この記事では2つの拡張子の使い分けについて説明します。

拡張子ex

拡張子 ex は、コンパイルを目的としたコードファイルに使います。

動的型付けのプログラミング言語というと、コンパイルなしで動作するインタプリタ方式のものが多いですが、Elixirは動的型付け言語、かつ、 コンパイラ方式 の言語です。

Elixirは仮想マシンで動作します。これは、Elixirのコードファイルは、仮想マシンが解釈できるバイトコードに変換されて動作する、ということ。そのため、Elixirのコードファイルはコンパイルされる必要があります。
簡単な例を見てみましょう。

defmodule Hello do
  def say(word) do
    IO.puts("Hello, #{String.capitalize(word)}!")
  end
end

Hello.say("world")

モジュール Hello を定義し、その中で関数 say を定義しています。モジュール Hello の外側で、定義した say 関数を呼び出しています。

このコードファイルをコンパイルしてみます。コンパイルには elixirc コマンドを使います。

$ elixirc hello.ex
Hello, World!

コンパイルと同時に、Hello.say("world") の実行結果が出力されますね。

コンパイルが成功すると、コンパイル結果のバイトコードファイルも生成されます。

$ ls -1
Elixir.Hello.beam
hello.ex

Elixir.Hello.beam という beam 拡張子のついたファイルがコンパイル結果のバイトコードファイルです。

Elixir.#{モジュール名}.beam というのがバイトコードファイル名の規則です。

中身を見てみましょうか。

FOR1tBEAMAtU8ÛElixir.Hello__info__
attributescompile
deprecatedexports_md5	functionsmacrosmd5moduleerlangget_module_infosay
Elixir.String
capitalizeElixir.String.Chars	to_string	byte_sizeall	Elixir.IOputsmodule_infoCodeÒ¬™" ;2uBuRebUrE‚e’u¢50@@@GP@G`@p@@N €™Ò¬™ @5¥@=µ ¬ °|0om \pZ
€\p@@À™
Ð@NPà™

こんな感じです。まあ、バイトコードなので基本的に中身は人間が読めるものではありません。(幽かに読めそうな雰囲気もあるかなきか。。。)

1つ実験をしてみましょう。次のコードファイルをコンパイルしてみます。

hello = fn word -> IO.puts "Hello, #{String.capitalize(word)}!" end
hello.("world")

モジュールを定義せずにインラインで(無名)関数を定義し、実行しています。処理自体は最初のモジュール Hello のバージョンと変わりはありません。

このコードファイルをコンパイルします。

$ elixirc hello2.ex
Hello, World!

関数の処理は出力されましたね。では、バイトコードファイルは出力されているでしょうか?

$ ls -1
hello2.ex

上記の通り、バイトコードファイルは生成されていません。

次のことがわかりました。

  • モジュールが定義されていなければバイトコードファイルは生成されない

コンパイルしてバイトコードファイルを生成するには、モジュールを定義しなければいけない、と言い換えることもできます。

コードファイルの拡張子を ex にすることは、次の意味があります。

  • コードファイルをコンパイルしてバイトコードを生成したい、という意思表示である。

ちなみにコンパイルするためのコードファイルの拡張子を ex にするのは、あくまでも慣習 です。たとえば拡張子を hoge など別のものにしても、モジュールが定義されていればコンパイルすることができます。

$ elixirc hello.hoge
Hello, World!

$ ls -1
Elixir.Hello.beam
hello.hoge

(だからといって特別な理由もなく、わざわざ慣習を破る必要はありませんけどね)

拡張子exs

拡張子 exssscripts です。

コードファイルに拡張子 exs をつける場合は、 バイトコードの出力を目的にしない ことを意味します。

スクリプトモード とも呼ばれますが、コードファイルの中の処理が実行されて、バイトコードを出力せずに処理を終えます。

スクリプトモード での実行は、 elixir コマンドを使います。

# goodbye.exs
defmodule Goodbye do
  def say(word) do
    IO.puts("Goodbye, #{String.capitalize(word)}!")
  end
end

Goodbye.say("world")

実行してみましょう。

$ elixir goodbye.exs
Goodbye, World!

バイトコードファイルは出力されません。

$ ls -1
goodbye.exs

Elixir.Goodbye.beam はありませんね。

elixir コマンドの動きについて、少し詳しく説明します。

elixir コマンドを実行したとき、バイトコードファイルは出力されませんが、バイトコード 自体は生成されて仮想マシンのメモリ上に展開されます。仮想マシンは展開されたバイトコードを実行して、結果を出します。しかし、 繰り返しますが、 バイトコードファイル は出力しません。

拡張子 exs のコードファイルを使う主な用途はテストです。テストコードのバイトコードファイルは本番運用のサービスには必要ないので、出力されなくていいわけです。

拡張子 ex の場合と同じですが、 exs もあくまで慣習です。だから、拡張子が exs だからといってバイトコードファイルが作成できないわけではありません。

# elixirc コマンドを使うと `exs` のコードファイルでもバイトコードを生成できる
$ elixirc goodbye.exs
Goodbye, World!

$ ls -1
Elixir.Goodbye.beam
goodbye.exs

拡張子 ex, exs は、コードを書く人間がコードファイルをどのような目的で書いたのは意思表示するもの、と考えるとよいでしょう。

まとめ

この記事の内容をまとめます。

  • 拡張子を ex にするか exs にするかは、目的で分ける。
  • 張子を ex にするのは、バイトコードファイルを生成するのが目的のコードファイル。
  • ただし、バイトコードを生成できるのはモジュール定義のあるコードファイルである(つまり、ex をつけるということは必然的にモジュールが定義されているということを意味する)。
  • 拡張子 exs はスクリプト用途。バイトコードファイルは生成しない。
  • テストコードには exs を使う。テストコードのバイトコードファイルは必要がないため。

以上です。

参考