Elixir でコールバックってどうやって実装しているのだろうと調べてみたら、マクロで実現されていました。なるほど。 その昔 C 言語でマクロを使ってコードを生成していたのを思い出した。
そんなわけで。マクロを使って「どう書く」のテストを実行するモジュールを書いてみた。
Elixir のドキュメントにテストの説明があったので、だいたいそのまま。
テストランナ
結果判定をする通常の関数 judge
、use
されたときに実行される __using__
マクロ、個々のテストを定義する test
マクロ、コンパイル時に実行される __before_compile__
マクロからなっています。
__before_compile__
が実行されるようにするには、@before_compile
で指定する必要があるようです。
defmodule Doukaku.TestRunner do def judge(_, expected, expected) do IO.puts "\x1b[32mpassed\x1b[0m" end def judge(input, expected, actual) do IO.puts "\x1b[31mfailed input: #{input}, expected: #{expected}, actual: #{actual}\x1b[0m" end defmacro __using__(_) do quote do import Doukaku.TestRunner @tests [] @before_compile Doukaku.TestRunner end end defmacro test(input, expected) do quote do @tests [{unquote(input), unquote(expected)}|@tests] end end defmacro __before_compile__(_) do quote do def run do @tests |> Enum.reverse() |> Enum.each(fn {input, expected} -> judge(input, expected, solve(input)) end) end end end end
どう書いたコードの例
英字の小文字だったばあいに大文字に変換するとかそういうの。
テストを定義する部分は CapitarlTest
などに分離するのがきれいなのかもしれません。
defmodule Capitarl do use Doukaku.TestRunner def to_capital(c) do c - 32 end def solve(input) do input |> String.to_charlist |> Enum.map(&to_capital/1) |> List.to_string end test("a", "A") test("ab", "AB") test("Ab", "AB") end Capitarl.run
実行
本当はパッケージにまとめておくのが正解の気がしますが、今回はファイル 2 つだけですまそうと。
テストランナをコンパイルします。
$ elixirc doukaku/test_runner.ex
どう書いたコードを実行します。
$ elixir capital.exs passed passed failed input: Ab, expected: AB, actual: !B
いつか読むはずっと読まない:どこかで聞いた気がする
懐かしいとはちょっとちがう、どこかで聞いた気がする、という感じが集まった一冊。
一番にそれを感じたのが最後の防火水槽のくだり。

- 作者: coco,日高トモキチ,玉川数
- 出版社/メーカー: KADOKAWA
- 発売日: 2017/06/01
- メディア: 単行本
- この商品を含むブログ (4件) を見る
なお、「今日の早川さん」の第 4 集が控えているらしい。
早川書房の打ち合わせは早川さん4巻だよ。お待たせしすぎたのでもう言っちゃえ。
— coco@『里山奇談』発売中 (@coco_n) 2017年4月21日

- 作者: coco
- 出版社/メーカー: 早川書房
- 発売日: 2016/05/31
- メディア: Kindle版
- この商品を含むブログを見る

- 作者: coco
- 出版社/メーカー: 早川書房
- 発売日: 2016/05/31
- メディア: Kindle版
- この商品を含むブログを見る

- 作者: coco
- 出版社/メーカー: 早川書房
- 発売日: 2016/05/31
- メディア: Kindle版
- この商品を含むブログを見る