pythonもSchemeも中途半端な状態で別の言語に手を出すなどアレですが、elixirとかちょっとぺろぺろしたくなるのでやっています。
んで、私はお脳が足りなくてrequire
, import
, use
がごっちゃになりがちで毎回調べてるのでメモ。
ここに載ってるし、本文とか転記する程度。
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
はマクロに限らず関数の呼び出しを手軽に行うものです。
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:
は指定した関数・マクロのみをimport
します。
オプションの値として以下のものがあります。
:functions
:macros
[<name>: <arity>]
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
する際にそれぞれ指定してやると以下の様になります。
:functions
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
:macros
iex(1)> import Hoge, only: :macros
nil
iex(2)> hoge1 "hoge"
** (RuntimeError) undefined function: hoge1/1
iex(2)> hoge_unless(false, "value")
"value"
[name: arity]
iex(1)> import Hoge, only: [hoge1: 1]
nil
iex(2)> hoge1 "hoge"
"hoge hoge1"
iex(3)> hoge2 "fuga"
** (RuntimeError) undefined function: hoge2/1
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
についてはよくわかってないです(;´Д`)
実装を見ると以下の様になっています。
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
とありますが、なんのことやらさっぱりなので引用した意味が全くないですね。
まぁ使っていればそのうち必要になるでしょうし、その時にわかるのではないでしょうか。
やっていきましょう。