작지만 유용한 erlang 매크로
Proplist는 매우 편리한 파라메터 사용 방식인데 보통 이런 식이다.
foo(Props) ->
Value = proplist:get_value(key, Props, "default"),
io:format("~p~n", [Value]).
그런데 OTP를 하다보면 모듈의 init/1 콜백에서 proplist를 받은 다음, 값을 하나하나 꺼내서 미리 정의한 레코드를 채운 후에 모듈 내부에서는 레코드만 사용하는 경우가 많다. 이런 식으로.
-behavior(gen_server).
-record(state, {name, age}).
init(Props) ->
State = #state{name = proplists:get_value(name, Props, ""),
age = proplists:get_value(age, Props)},
{ok, State}.
이 짓, 많이 하다보니 바보같다. 필드가 열개쯤 되면 더욱 바보같다. 백번쯤 하고서야 이런걸 만들게 된다.
%% file: props_to_record.hrl
-define(PROPS_TO_RECORD(Props, Record), ?PROPS_TO_RECORD(Props, Record, #Record{})()).
-define(PROPS_TO_RECORD(Props, Record, Default),
fun() ->
Fields = record_info(fields, Record),
[Record | Defaults] = tuple_to_list(Default),
List = [proplists:get_value(F, Props, D) || {F, D} <- lists:zip(Fields, Defaults)],
list_to_tuple([Record | List])
end).
8줄짜리 매크로, 이제는 이것만큼 자주 쓰는 도구도 드물다. Proplist와 레코드가 차이가 좀 있어도 문제없다. 레코드에 없는 property는 무시되고, proplist에 없는 필드는 기본값으로 남겨진다.
-behavior(gen_server).
-include("props_to_record.hrl").
-record(state, {name = "", age}).
init(Props) ->
{ok, ?PROPS_TO_RECORD(Props, state)}.
필요는 발명의 어미니라더니. 백번이 아니라 열번만에 이걸 만들 생각을 하게 되는게 발전이겠지.