6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Elixir]マクロで関数を展開する一例

Last updated at Posted at 2015-08-30

Goal

Elixirのマクロで、関数を展開するサンプルを作成する。

Dev-Environment

OS: Windows8.1
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.5

Wait a minute

このサンプルは、マクロで関数を生成し展開していくサンプルです。

このサンプル自体で何かができるものではありません。
予め、ご了承下さい。

Index

Macro to expand the function
|> Plan
|> First step
|> Second step
|> Brief description

Plan

やりたいことを最初に記述します。

以下のような記述をすると、
alias_name:の値が関数名になり、マクロが展開されていくようにしたい。

defmodule UseSample do
  use Sample

  get "path/to/hoge", "ActionModule", [alias_name: "hoge"]
  get "path/to/huge", "ActionModule", [alias_name: "huge"]
end

上記のalias_name:だと、
hoge_def/1、huge_def/1といったように・・・関数として展開していって欲しい。

First step

第一段階のソースコードを作成します。

Example:

defmodule Sample do
  defmacro __using__(_options) do
    quote do
      import unquote(__MODULE__)
    end
  end

  defmacro match(_http_method, _path, _action, opts) do
    name = opts[:alias_name]
  
    quote do
      def unquote(String.to_atom "#{name}_def1")(arg) do
        IO.inspect "#{unquote(name)}_def1: #{arg}"
      end

      def unquote(String.to_atom "#{name}_def2")(arg) do
        IO.inspect "#{unquote(name)}_def2: #{arg}"
      end
    end
  end
end

defmodule UseSample do
  use Sample

  match :get, "path/to/hoge", "ActionModule", [alias_name: "hoge"]
end

Result:

iex> import UseSample
nil
iex> hoge_def1(1)
"hoge_def1: 1"
"hoge_def1: 1"
iex> hoge_def2(1)
"hoge_def2: 1"
"hoge_def2: 1"

関数として展開されている。
出力結果が変わらないよう注意しながら、もう少し手を加える。

Second step

第二段階のソースコードを記述します。

Example:

defmodule Sample do
  defmacro __using__(_options) do
    quote do
      Module.register_attribute __MODULE__, :routes, accumulate: true,
                                                     persist: false
      import unquote(__MODULE__)
      @before_compile unquote(__MODULE__)
    end
  end

  defmacro __before_compile__(env) do
    routes = Module.get_attribute(env.module, :routes)
  
    for route <- routes do
      {http_method, path, action, opts} = route
      quote do
        match(unquote(http_method), unquote(path), unquote(action), unquote(opts))
      end
    end
  end

  defmacro match(_http_method, _path, _action, opts) do
    name = opts[:alias_name]
    if name do
      quote do
        def unquote(String.to_atom "#{name}_def1")(arg) do
          IO.inspect "#{unquote(name)}_def1: #{arg}"
        end

        def unquote(String.to_atom "#{name}_def2")(arg) do
          IO.inspect "#{unquote(name)}_def2: #{arg}"
        end
      end
    end
  end

  defmacro get(path, action, opts) do
    add_route(:get, path, action, opts)
  end

  defp add_route(http_method, path, action, opts) do
    quote do
      @routes {unquote(http_method), unquote(path), unquote(action), unquote(opts)}
    end
  end
end

defmodule UseSample do
  use Sample

  get "path/to/hoge", "ActionModule", [alias_name: "hoge"]
  get "path/to/huge", "ActionModule", [alias_name: "huge"]
end

Result:

iex> import UseSample
nil
iex> hoge_def1(1)
"hoge_def1: 1"
"hoge_def1: 1"
iex> hoge_def2(2)
"hoge_def2: 2"
"hoge_def2: 2"
iex> huge_def1(1)
"huge_def1: 1"
"huge_def1: 1"
iex> huge_def2(2)
"huge_def2: 2"
"huge_def2: 2"

これで完成(?)です。

動作の流れについては次の項目。
(説明を全くしてなかったですね)

Brief description

簡単な動作の流れを記述します。

  1. UseSampleモジュールで、Sampleモジュールをuseしている (Sample.using/1が展開される)
  2. Sample.using/1では、アトリビュートの登録、インポート、Sample.before_compile/1の設定をしている
  3. UseSampleモジュールで、Sample.get/3マクロを呼び出している
  4. Sample.add_route/4では、アトリビュートの設定を行っている
  5. Sample.before_compie/1では、アトリビュートの値を取得し、Sample.match/4を呼び出し引数として渡している
  6. Sample.match/4は、alias_name:の値に応じた関数を展開している

簡単に流れを説明すると・・・こんな感じですね。
(説明足りなかったらコメント下さい)

Speaking to oneself

Phoenix-Framework v0.1.1のソースコードを参考にさせて頂きました。
参考にさせて頂いたソースコード(mapper.ex)ですが、最新だとソースコード自体が存在しませんでした。

同様の処理をどこかしらで行っているとは思いますが、
そこまで解読できていません・・・orz

Bibliography

Phoenix-Framework v0.1.1 - mapper.ex

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?