#naming #elixirlang 함수 이름이 _new 로 끝나면 뭐다?

1 minute read

eggs

iex> Map.put_new(%{limit: 10}, :limit, 20)
%{limit: 10}
iex> Map.put_new(%{order: :desc}, :limit, 20)
%{limit: 20, order: :desc}

Map.put_new/3 함수는 두 번째 인자로 받은 key가 첫 번째 인자로 받은 map에 없을 때만 추가한다. Keyword.put_new/3 함수도 대상이 map에서 keyword list로 바뀔 뿐 동작은 똑같다.

defmodule InfoSys do
  @backends [InfoSys.Wolfram]

  def compute(query, opts \\ []) do
    opts = Keyword.put_new(opts, :limit, 10) # 1
    backends = opts[:backends] || @backends # 2

    backends # 3
    |> Enum.map(&async_query(&1, query, opts)) # 4
  end
end

Programming Phoenix 1.4 책에 나온 예제를 발췌했다. 1번 표현식(expression)처럼 디폴트 값을 추가할 때, Keyword.put_new/3 함수를 유용하게 사용할 수 있다.

4번 async_query 함수 인자로 넘길 opts 변수에 디폴트 값을 추가하는 경우엔 1번 표현식처럼 Keyword.put_new/3 함수를 사용한다. 3번처럼 함수 내부에서 바로 사용하는 경우엔 2번 표현식처럼 backends 변수에 backends 키에 대한 값을 할당한다. 만약 nil이면 디폴트 값을 할당한다.

def compute(query, opts \\ []) do
  opts =
    opts
    |> Keyword.put_new(:limit, 10)
    |> Keyword.put_new(:backends, @backends)

  opts[:backends]
  |> Enum.map(&async_query(&1, query, opts))
end

디폴트 값을 Keyword.put_new/3 함수로 통일해서 넣는 것도 나쁘지 않다고 생각한다. 장점이 명확하지 않으면 방법을 통일하는 게 편하다. 이렇게 통일하고 다른 방법을 사용할 때는 주석으로 왜 이렇게 사용했는지 표시하면 된다. 찾는 비용이 커서 변수에 할당. 뭐 이런 식으로.

_new 단어로 끝나는 함수가 많을 줄 알고 글을 쓰기 시작했는데, 2개 밖에 없어서 당황했다.