LoginSignup
0

More than 5 years have passed since last update.

Elixirのdecoratorを使って関数に機能を追加する

Last updated at Posted at 2018-07-28

この記事では、Elixirの関数に付加的な機能を追加するライブラリである decorator について紹介します。
decoratorは、あらかじめ定義された処理を任意の関数へと追加するライブラリです。これにより、デバッグ用途の情報取得処理や何らかのチェック処理を簡単に追加することが可能になります。

使い方

それでは、decorator の使用方法を見てみましょう。

インストール

mix.exsdepsdecorator を追加して mix deps.get を実行します。

defp deps do
 [
   {:decorator, "~> 1.2"}
 ]
end

基本的な使い方

decorator は、次のように Decorator.Define マクロを使用して定義します。この例では、関数の情報やパラメータを出力する print_func_info という decorator を定義しています。

print_decorator.ex
defmodule PrintDecorator do
  use Decorator.Define, print_func_info: 0

  def print_func_info(body, context) do
    quote do
      # moduleや関数名などの情報を取り出す
      module = Atom.to_string(unquote(context.module))
      name = Atom.to_string(unquote(context.name))
      arity = unquote(context.arity)
      args = unquote(context.args)

      # 取得した情報を表示する
      IO.puts("[DEV] #{module}.#{name}/#{arity} is called.")
      IO.write("[DEV] args: ")
      IO.inspect(args)

      unquote(body)
    end
  end
end

定義した print_func_info は、次のコードのように @decorate と共に使用します。ここでは Greeting.say_goodbye/2 という関数に、情報出力機能を追加しています。

greeting.ex
defmodule Greeting do
  use PrintDecorator

  @decorate print_func_info()
  def say_goodbye(name, time) do
    IO.puts("It's #{time}. Goodbye #{name}.")
  end
end

iexで Greeting.say_goodbye/2 を実行すると、次のように出力されます。

iex(1)> Greeting.say_goodbye("Taro", "18:00")
[DEV] Elixir.Greeting.say_goodbye/2 is called.
[DEV] args: ["Taro", "18:00"]
It's 18:00. Goodbye Taro.
:ok

パラメータチェックの例

他の使用例として、関数へと渡されるパラメータの値をチェックさせる例を見てみましょう。

ここでは、パラメータの値に nil が含まれていれば :error を返す decorator を考えてみます。そのような動作をさせるため、次のように check_param_not_nil という decorator を定義します。

parameter_check.ex
defmodule ParameterDecorator do
  use Decorator.Define, check_param_not_nil: 0

  def check_param_not_nil(body, context) do
    quote do
      # パラメータにnilが含まれるかチェック
      args = unquote(context.args)
      has_nil = args |> Enum.any?(fn x -> x == nil end)

      if(has_nil) do
        # nilがあれば :error を返す
        {:error, :nil_param, args}
      else
        unquote(body)
      end
    end
  end
end

それでは、 check_param_not_nil を使ってみましょう。次のように、パラメータの値を加算するだけの関数 Foo.plus/3check_param_not_nil をセットします。

foo.ex
defmodule Foo do
  use ParameterDecorator

  @decorate check_param_not_nil()
  def plus(x, y, z) do
    result = x + y + z

    {:ok, result}
  end
end

iexで Foo.plus/3 を実行すると、次のように出力されます。

iex(1)> Foo.plus(1, 2, 3)
{:ok, 6}

iex(2)> Foo.plus(1, nil, 3)
{:error, :nil_param, [1, nil, 3]}

まとめ

この記事では、 decorator を使うことで任意の関数に簡単に新たな機能を追加できることを紹介しました。ただし、 Elixirの公式サイト でも述べられているように、マクロの濫用はコードの可読性低下や保守性の低下を招く恐れがあります。私見ではありますが、 decorator も必要な箇所だけで適切に用いるのが良いと思います。

参考

decorator
Elixir function decorator example

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
0