【初心者向け】世界一簡単な Elixir メタプロ入門

# 概要

プログラミングをやっているとかなりたどり着くメタプログラミング（以下、メタプロ）。コードをキレイに書きたい！とか、便利なライブラリ作りたい！とかなってくると絶対に避けられない道です。

本稿では、Elixir でメタプロをするにあたって基本的な概念とコードを紹介します。

# メタプロとは

そもそもメタプロとは何でしょうか？

wikipedia によると、

> メタプログラミング (metaprogramming) とはプログラミング技法の一種で、ロジックを直接コーディングするのではなく、あるパターンをもったロジックを生成する高位ロジックによってプログラミングを行う方法

ref. [メタプログラミング | wikipedia](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%82%BF%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)

とのことです。めっちゃ噛み砕いて言うと、プログラミングでプログラミングを書く手法みたいなことです。

# Elixir でメタプロ

では、さっそく Elixir でメタプロをどうやっていくのかを紹介します。

## Quote

一番の基本は `Quote` と呼ばれるものです。 `Quote` は Elixir のプログラムをそのまま評価します。

たとえば、以下のコード。hoge という関数を定義しました。

```elixir 

defmodule QuoteTest do
  def hoge do
    IO.puts 'hoge だよ'
  end
end

```

これと全く同じものを quote を使って書くと以下のようになります。

```elixir 

defmodule QuoteTest do
  # defmacro の説明は後ほど行います。
  defmacro hoge do
    quote do
      IO.puts 'hoge だよ'
    end
  end
end

```

これは何をやっているかというと、`quote` のブロックで囲まれた部分をそのまま文字通りに評価しているのです。Elixir は `quote` で囲まれたところを Elixir のプログラミングとして認識してくれると覚えておきましょう。

実際に動かしてみると以下のようにちゃんと動作します。

```
iex> require QuoteTest
iex> QuoteTest.hoge()
hoge だよ
:ok
```

## Unquote 

`quote` の次に重要なのが `unquote` です。`unquote` は `quote` の式を修正する際に利用するものです。

たとえば、以下のような挨拶をする関数を定義します。

```elixir

defmodule UnquoteTest do
  def hello do
    IO.puts 'こんにちは'
  end
end

```

さきほどの要領でまずは puts の部分を quote で囲いあえてメタプロしていきます。

```diff

defmodule UnquoteTest do
  defmacro hello do
+    quote do
+      IO.puts 'こんにちは'
+    end
  end
end

```
puts する内容を式で評価して変更したい場合どうしたらよいでしょうか。こういった場合に unquote を使います。

```elixir

defmodule UnquoteTest do
  defmacro hello do
    name = 'test'
    quote do
      hello = "こんにちは #{unquote(name)}"
      IO.puts(hello)
    end
  end
end

```

```
iex> UnquoteTest.hello()
こんにちは test
:ok
```

`unquote` は引数で渡されたものを評価します。そして評価されたものを Elixir のコードとして認識するようになります。

なので上のコードは Elixir では以下のように認識されています。

```elixir

defmodule UnquoteTest do
  def hello do
    hello = "こんにちは test"
    IO.puts(hello)
  end
end

```

逆にこの場合、`unquote` を使わないとどうなるかというと、`name` は変数の name としてだけ扱われます。そして quote 内で name は定義されていないので `undefined function name/0` となります。以下のような状態です。

```elixir

defmodule UnquoteTest do
  def hello do
    hello = "こんにちは #{name}"
    IO.puts(hello)
  end
end

```

### unquote による関数の定義

また unquote の便利な使い方を紹介します。関数を定義することです。unquote の引数は評価されてから　Elixir の式として認識されるようになるので以下のように利用することができます。

```elixir

defmodule MacroTest do
  Enum.each ['mike', 'alice', 'bob'], fn name ->
    def unquote(:"hello_#{name}")() do
      IO.puts("hello #{unquote(name)}")
    end
  end
end

```

```
iex> MacroTest.hello_mike
hello mike
:ok
```

each によってわかってきた文字列を atom に変えて、それを unquote を使って elixir のコードとして認識されているのです。

## マクロ

さいごにマクロ。

> In the simplest of terms macros are special functions designed to return a quoted expression that will be inserted into our application code. （マクロを簡単に表現すると、アプリケーションコードに挿入されるquoteで囲まれた式を返す関数）
ref. https://elixirschool.com/jp/lessons/advanced/metaprogramming/

もっと簡単にいうと、quote と unquote を使って作った関数ということです。
ここでは詳しく見ていきませんが、quote と unquote を理解できれば、理解するのは難しくはありません。

Elixir Schoolの [マクロ](https://elixirschool.com/jp/lessons/advanced/metaprogramming/#マクロ)を参考にするのがおすすめです。


# 参考文献

[メタプログラミング | Elixir School](https://elixirschool.com/jp/lessons/advanced/metaprogramming/)
[How to create a dynamic function name using Elixir macro? | Stackoverflow](https://stackoverflow.com/questions/30498528/how-to-create-a-dynamic-function-name-using-elixir-macro)