スポンサーリンク

2017年4月12日

alchemist report 008

alchemist report 008

ElixirのMap(構造体)とASTを使った、就業時間中の暇つぶし内容。
プロジェクト作る。

Example:

> mix new map_and_macro_example
> cd map_and_macro_example
> mix test
ただMap使うんではつまらんので構造体を扱うため、defstructのみを定義したモジュールを作成。

File: lib/User.ex

defmodule User do
  defstruct [:name, :email]
end
ちょっとASTにしたりして遊ぶ。

Example:

iex> %User{name: "hoge", email: "hoge@test.com"}
%User{email: "hoge@test.com", name: "hoge"}
iex> quote do: %User{name: "hoge", email: "hoge@test.com"}
{:%, [],https://gist.github.com/darui00kara/eddf82011b1d99dd166ffb1438b0343b/edit
 [{:__aliases__, [alias: false], [:User]},
  {:%{}, [], [name: "hoge", email: "hoge@test.com"]}]}
さらに遊ぶ。
iex> user = %User{name: "hoge", email: "hoge@test.com"}
%User{email: "hoge@test.com", name: "hoge"}
iex> %User{user | name: "huge"}
%User{email: "hoge@test.com", name: "huge"}
iex> quote do: %User{user | name: "huge"}
{:%, [],
 [{:__aliases__, [alias: false], [:User]},
  {:%{}, [], [{:|, [], [{:user, [], Elixir}, [name: "huge"]]}]}]}
iex> module_name = :User
:User
iex> struct_value = :user
:user
iex> name = "huge"
"huge"
iex> q = {:%, [], [{:__aliases__, [alias: false], [module_name]}, {:%{}, [], [{:|, [], [{struct_value, [], Elixir}, [name: name]]}]}]}
{:%, [],
 [{:__aliases__, [alias: false], [:User]},
  {:%{}, [], [{:|, [], [{:user, [], Elixir}, [name: "huge"]]}]}]}
iex> Macro.to_string(q)
"%User{user | name: \"huge\"}"
iex> quote do: {:%, [], [{:__aliases__, [alias: false], [module_name]}, {:%{}, [], [{:|, [], [{struct_value, [], Elixir}, [name: name]]}]}]}
{:{}, [],
 [:%, [],
  [{:{}, [], [:__aliases__, [alias: false], [{:module_name, [], Elixir}]]},
   {:{}, [],
    [:%{}, [],
     [{:{}, [],
       [:|, [],
        [{:{}, [],
          [{:struct_value, [], Elixir}, [],
           {:__aliases__, [counter: 0], [Elixir]}]},
         [name: {:name, [], Elixir}]]]}]]}]]}
iex> quote do: unquote({:%, [], [{:__aliases__, [alias: false], [module_name]}, {:%{}, [], [{:|, [], [{struct_value, [], Elixir}, [name: name]]}]}]})
{:%, [],
 [{:__aliases__, [alias: false], [:User]},
{:%{}, [], [{:|, [], [{:user, [], Elixir}, [name: "huge"]]}]}]}
おまけ長い(笑)
せっかくなので、マクロでMapをASTで引数にとるものを作る。

File: lib/macro_example.ex

defmodule MacroExample do
  alias User

  defmacro get_struct_info(
    {:%, _, [{_, _, struct_name}, {:%{}, _, values}]}) do

    quote do
      {unquote(struct_name), unquote(values)}
    end
  end

  def user(name, email) do
    get_struct_info %User{name: name, email: email}
  end
end
構造体の名前とキーバリューをタプルにして返すだけ。
別段なにもやっていない。
(そのまま使うのが面倒なんで、同じモジュール内に関数を用意して使う)
動作確認

Example:

iex> MacroExample.user "hoge", "hoge@test.com"

{[:User], [name: "hoge", email: "hoge@test.com"]}
問題なし。
しかし、これだけじゃ他の構造体で動作するかわからないので、もいっこ構造体を定義して確かめてみる。

File: lib/Post.ex

defmodule Post do
  defstruct [:title, :body]
end

File: lib/macro_example.ex

defmodule MacroExample do
  alias User
  alias Post

  ...

  def post(title, body) do
    get_struct_info %Post{title: title, body: body}
  end
end
動作確認。

Example:

iex> MacroExample.post "hogehoge", "foobar"

{[:Post], [title: "hogehoge", body: "foobar"]}
ここで時間切れになった。
「おーい、〜くん。これ頼める?」
「了解です。(就業時間終了間際に仕事頼みにこないでほしい・・・)」
以上。

人気の投稿