Elixir key-value 자료구조 함수 네이밍 규칙 - get, fetch, fetch!
Elixir 표준 라이브러리 중 key-value 자료구조에 규칙을 가진 것처럼 보이는 함수가 있다. get
, fetch
, fetch!
와 같은 값을 가져오는 함수이다. 자료 구조에서 값을 가져오는 함수로 fetch
를 사용하는 게 낯설었다. 어떤 규칙으로 설계했는지 궁금해졌다.
요약 by human
- Elixir에서 key-value 자료구조에서 값을 가져오는 함수가 3개
get
- 없으면 default 값fetch
- {:ok, value} 형식으로 패턴매칭하기 좋게 리턴fetch!
- 없으면 에러를 내는 터프한 함수
- Erlang은
get
,find
이름을 사용 - Erlang 네이밍 규칙이 더 마음에 든다
get
은 빠르게 값을 가져오고fetch
는 DB나 웹에서 가져오는 식으로 이름을 붙여왔음
get
, fetch
, fetch!
구분
When you see the functions
get
,fetch
, andfetch!
for key-value data structures, you can expect the following behaviours:
get
returns a default value (which itself defaults to nil) if the key is not present, or returns the requested value.fetch
returns :error if the key is not present, or returns {:ok, value} if it is.fetch!
raises if the key is not present, or returns the requested value.
예전에는 이런 네이밍 규칙(naming convention) 문서가 없었던 것 같다. 표준 라이브러리 문서 보강을 많이 했다더니 정말이다.
’없을 수도 있지’의 get
함수. 없을 때는 디폴트 값을 리턴한다.
iex> Keyword.get([], :a)
nil
iex> Keyword.get([a: 1], :a)
1
iex> Keyword.get([a: 1], :b)
nil
iex> Keyword.get([a: 1], :b, 3)
3
세 번째 인자로 디폴트 값을 넘긴다.
’패턴 매칭 편하게 해줄께’의 fetch
함수.
iex> Keyword.fetch([a: 1], :a)
{:ok, 1}
iex> Keyword.fetch([a: 1], :b)
:error
{:ok, value}
를 리턴해서 패턴 매칭을 편하게 할 수 있다.
case Keyword.fetch([a: 1], :a) do
{:ok, val} -> IO.puts("찾았다! #{val}")
:error -> IO.puts("없다!")
end
fetch
함수가 아니라 get
함수를 호출한다면 디폴트 값을 리턴한 것인지 찾은 key의 값인지 구분할 수가 없다. has_key?
함수와 같이 써야 확신할 수 있는데, fetch
함수를 쓰면 한 번에 가능하다.
’없으면 큰일난다’의 fetch!
함수
iex> Keyword.fetch!([a: 1], :a)
1
iex> Keyword.fetch!([a: 1], :b)
** (KeyError) key :b not found in: [a: 1]
get
과 fetch
의 사전적 의미
to obtain (something) (무언가를) 얻다
to go after and bring back (someone or something) 누군가나 어떤 것을 찾아가서 다시 데려오거나 가지고 돌아오다
get
과 다르게 fetch
는 찾아가서 가지고 돌아온다는 뜻이다. 그래서 더 시간이 걸리는 뉘앙스를 전달할 수 있다. 뭔가 찾아서 가져온다는 뜻이니 반드시 가져온다는 뉘앙스도 전달할 수 있을 것 같다.
Erlang은 어떤 함수 이름을 사용하나? - get
, find
fetch
라는 함수 이름을 Erlang에서 가져왔다고 생각했으나 보기 좋게 빗나갔다.
Elixir의 fetch
는 Erlang의 find
와 비슷하다.
1> Map = #{foo => 42}.
2> maps:find(foo, Map).
{ok,42}
3> maps:find(bar, Map).
error
Elixir의 get
은 디폴트 인자를 명시적으로 넘긴 get
과 비슷하다.
1> Map = #{foo => 42}.
2> maps:get(foo, Map, 0).
42
3> maps:get(bar, Map, 0).
0
Elixir의 fetch!
는 디폴트 인자가 없는 get
과 비슷하다.
1> Map = #{foo => 42}.
2> maps:get(foo, Map).
42
3> maps:get(bar, Map).
** exception error: {badkey,bar}
Erlang의 get
, find
규칙이 더 마음에 든다
없는 영어 살림에 뭔가 찾아서 가져와서 오래 걸릴 수 있다는 fetch
라는 용어를 허탈하게 써버려서 아깝다는 생각이 들었다. get
은 캐시가 있어서 바로 리턴할 수 있고 fetch
는 웹이나 DB 등에서 가져오지만 느리다는 뜻으로 사용하고 있었기 때문이다.
find
는 찾으니깐 없을 수도 있지. 이런 뉘앙스를 전달해서 Erlang의 네이밍 규칙이 더 마음에 든다.
Erlang에도 fetch
를 안 쓰는데 왜 이런 단어를 사용했을까? Ruby와 관련이 있을 수 있겠다고 생각했다. Elixir는 Ruby의 영향을 많이 받았다. Elixir를 만든 Jose Valim이 Rails 코어 팀 멤버였기 때문이다. Ruby hash에서 사용하는 fetch
함수에서 가져오지 않았을까?
마치며
Elixir에서 사용하는 get
, fetch
, fetch!
함수가 처음에는 혼란스러웠다. 값을 가져오는 함수가 왜 이렇게 많아? 패턴매칭에 유용해서 fetch
함수의 진가를 알아봤고 그 외에는 get
함수를 편하게 사용하고 있다. fetch!
함수는 쓸 일이 없는데, 언젠가는 쓸 일이 있을 것 같다.
Erlang의 get
, find
규칙이 더 마음에 든다. fetch
단어를 빠르게 값을 가져오는 get
과는 달리 시간이 오래 걸리는 작업을 나타내는 데 쓰고 있기 때문이다.
뭐 그래도 규칙을 잘 정하고 지키기만 한다면 잘 따르다 보면 금방 익숙해진다. Jose Valim도 처음엔 Ruby hash의 fetch 함수 이름이 마음에 들지 않았을 것 같다는 재미있는 생각이 들었다.