Macro 모듈
Macro 모듈
Macro 모듈은 AST를 다루고 매크로를 구현하는 함수들을 제공해요. 매크로는 컴파일 타임 구성요소로, Elixir의 AST를 입력으로 받아 Elixir의 AST를 출력으로 돌려줍니다.
이 모듈의 많은 함수는 정확히 Elixir AST를 순회·조회·변환하기 위해 존재해요.
함수와 매크로의 차이를 보여주는 간단한 예를 볼게요.
defmodule Example do
defmacro macro_inspect(value) do
IO.inspect(value)
value
end
def fun_inspect(value) do
IO.inspect(value)
value
end
end
정수를 인자로 넘기면 둘은 같게 동작해요. 하지만 표현식을 넘기면 달라져요.
macro_inspect(1 + 2)
#=> {:+, [line: 3], [1, 2]}
#=> 3
fun_inspect(1 + 2)
#=> 3
#=> 3
매크로는 인자로 주어진 **코드의 표현(AST)**을 받고, 함수는 코드의 실행 결과를 받아요. 매크로는 코드 표현의 상위집합을 돌려줘야 해요. 자세한 내용은 t:input/0과 t:output/0을 참고하세요.
Elixir AST와 그 구성에 대해 더 배우려면 quote/2를 보세요.
코드 평가 {: .tip}
이 모듈의 함수는 코드를 평가하지 않아요. 사실 매크로에서 코드를 평가하는 것은 종종 안티 패턴이에요. 코드 평가는
Code모듈을 참고하세요.
출처: Macro
본문
주요 타입
@type input() ::
input_expr() | {input(), input()} | [input()] | atom() | number() | binary()
매크로의 입력이에요.
@type metadata() :: keyword()
AST 메타데이터의 키워드 리스트예요. Elixir AST의 메타데이터는 값들의 키워드 리스트예요. 어떤 키든 쓸 수 있고 컴파일러의 다른 부분이 다른 키를 쓸 수 있어요. 예를 들어 매크로가 받는 AST는 항상 :line 어노테이션을 포함하지만, quote/2가 만들어낸 AST는 특정 조건에서만 :line을 가져요.
@type escape_opts() :: [
unquote: boolean(),
prune_metadata: boolean(),
generated: boolean()
]
주요 함수
expand/2, expand_once/2, expand_literals/2
expand(ast, env)
expand_once(ast, env)
expand_literals(ast, env)
expand/2는 매크로를 재귀적으로 모두 펼치는 반면, expand_once/2는 가장 바깥쪽 한 번만 펼쳐요. expand_literals/2는 리터럴을 해당 문맥에서 펼쳐요(예: 매크로가 함수 안에서 쓰일 모듈 참조를 확장할 때 %{__CALLER__ | function: {:call, 2}}같은 환경을 넘김).
escape/2
escape(term, opts \ [])
term을 AST로 변환해요. 옵션으로 :unquote, :prune_metadata, :generated를 넘길 수 있어요.
decompose_call/1
decompose_call(ast)
decompose_call(ast, arity)
호출을 {function, arguments} 또는 {module, function, arguments}로 분해해요. 분해할 수 없으면 :error를 돌려줍니다.
pipe/3
pipe(expr, call, position \ :left)
expr |> call 형태의 파이프라인 AST를 만들어요. position은 파이프된 인자가 왼쪽(:left)인지 오른쪽(:right)인지 정해요.
postwalk/2, prewalk/2, postwalk/3, prewalk/3
prewalk(ast, fun)
postwalk(ast, fun)
AST를 전위(prewalk) 또는 후위(postwalk) 순회하며 주어진 함수를 적용해 변환해요. 접기(fold)를 같이 적용하는 /3 변형도 있어요.
generate_arguments/2, var/2, uniq/1, struct!/2, to_string/2, inspect_atom/2
generate_arguments(amount, context)— 주어진 컨텍스트의 고유한 변수 인자 리스트를 만들어요.var(atom, context)— 변수 노드를 만들어요.uniq(ast)— AST에서 중복된 리터럴을 제거해요.struct!(module, env)— 주어진 모듈의 구조체를 컴파일 타임에 만들어요(환경에서 구조체 필드를 검사).to_string(ast, fun)— AST를 문자열로 변환해요.inspect_atom(atom, opts)— inspect 옵션에 따라 아톰 문자열을 만드는 데 쓰여요.
has_sequence?/1, update_meta/2, traverse/4, validate/2
has_sequence?/1은 AST에 시퀀스(리스트)가 있는지 검사하고, update_meta/2는 AST 노드의 메타데이터를 갱신해요. traverse/4는 AST를 순회하며 주어진 함수로 변환하고, validate/2는 주어진 AST가 합법적인지 검증해요.
더 알아보기
Kernel.SpecialForms.quote/2: 매크로에서 코드를 만드는 특수 형식Kernel.SpecialForms.unquote/1: quote 안에서 표현식을 다시 평가Code모듈: 코드 평가와 컴파일- Meta-programming anti-patterns: 매크로 사용 시 피해야 할 함정