elixirのrequire, import, use

elixir
2014/09/28

pythonもSchemeも中途半端な状態で別の言語に手を出すなどアレですが、elixirとかちょっとぺろぺろしたくなるのでやっています。

んで、私はお脳が足りなくてrequire, import, useがごっちゃになりがちで毎回調べてるのでメモ。

ここに載ってるし、本文とか転記する程度。

require

document

elixirにはマクロ機能があってコンパイル時に実行・展開されます。

で、コンパイルおよびロード時にそのマクロが正しく実行・展開されるかを担保するのがrequireというわけです。

ただ、基本的にはrequireをする必要はなくモジュールで定義されたマクロを使いたい時に必要となる感じのもので、そういう時に明示的にrequireをするとかそんな感じっぽい。

iex(3)> Integer.is_odd 3
** (CompileError) iex:3: you must require Integer before invoking the macro Integer.is_odd/1
(elixir) src/elixir_dispatch.erl:110: :elixir_dispatch.dispatch_require/6
iex(3)> require Integer
nil
iex(4)> Integer.is_odd 3
true

elixirのInteger.is_odd/1はガードとして使えるマクロですが、そんなマクロ知らないからIntegerモジュールをrequireせえやとエラーを出しています。

requireはマクロを利用したい時にモジュールを読み込むディレクティブということでしょう。

あと、ちなみにレキシカルスコープが有効なので特定の関数内だけでみたいなこともできます。

また、オプションとしてas:が指定出来ます。as:を指定してやるとエイリアスが効いて全体的に良くなります。(alias参照)

import

document

importはマクロに限らず関数の呼び出しを手軽に行うものです。

iex(1)> List.flatten([1, [2], 3])
[1, 2, 3]
iex(2)> flatten([1, [2], 3])
** (RuntimeError) undefined function: flatten/1

iex(2)> import List
nil
iex(3)> flatten([1, [2], 3])
[1, 2, 3]

こんな風に使います。Listモジュールのflatten/1関数を直接使える様になりました。

で、デフォルトではimportすると対象のモジュールの持つ関数・マクロすべてが参照されますがオプションとしてonly:, except:を指定するとセレクタとして使用出来ます。

only:

only:は指定した関数・マクロのみをimportします。

オプションの値として以下のものがあります。

defmodule Hoge do
  def hoge1(a) do
    a <> " hoge1"
  end

  def hoge2(a) do
    a <> " hoge2"
  end

  defmacro hoge_unless(pred, expr) do
    quote do
      if(!unquote(pred), do: unquote(expr))
    end
  end
end

というコードがあったとして、importする際にそれぞれ指定してやると以下の様になります。

iex(1)> import Hoge, only: :functions
nil
iex(2)> hoge1 "hoge"
"hoge hoge1"
iex(3)> hoge_unless(false, "value")
** (RuntimeError) undefined function: hoge_unless/2
iex(1)> import Hoge, only: :macros
nil
iex(2)> hoge1 "hoge"
** (RuntimeError) undefined function: hoge1/1

iex(2)> hoge_unless(false, "value")
"value"
iex(1)> import Hoge, only: [hoge1: 1]
nil
iex(2)> hoge1 "hoge"
"hoge hoge1"
iex(3)> hoge2 "fuga"
** (RuntimeError) undefined function: hoge2/1

except:

except:only:の逆です。

iex(1)> import Hoge, except: [hoge1: 1]
nil
iex(2)> hoge1 "hoge"
** (RuntimeError) undefined function: hoge1/1

iex(2)> hoge2 "hoge"
"hoge hoge2"
iex(3)> hoge_unless(false, "hoge")
"hoge"

まぁ、直感的に判りますね。

use

document

useについてはよくわかってないです(;´Д`)

実装を見ると以下の様になっています。

defmacro use(module, opts \\ []) do
  expanded = Macro.expand(module, __CALLER__)

  case is_atom(expanded) do
    false ->
      raise ArgumentError, "invalid arguments for use, expected an atom or alias as argument"
    true ->
      quote do
        require unquote(expanded)
        unquote(expanded).__using__(unquote(opts))
      end
  end
end

どうやらマクロをexpandしたものを__using__しているrequireの糖衣構文みたいですね。

__using__ってなんだろうか(;´Д`)

ここをみると,

Allow a developer to use this module in their programs with the following options:

:only_operators - include only operators

:skip_operators - skip operators

とありますが、なんのことやらさっぱりなので引用した意味が全くないですね。

まぁ使っていればそのうち必要になるでしょうし、その時にわかるのではないでしょうか。

やっていきましょう。

戻る