Erlang 라이브러리

Erlang 라이브러리

Elixir는 Erlang 라이브러리와 아주 훌륭하게 상호 운용돼요. 사실 Elixir는 Erlang 코드를 그냥 감싸는 것보다, Erlang 코드와 직접 인터페이스하는 방식을 권장해요. 이 절에서는 Elixir에는 없지만 Erlang에서 유용하게 쓸 수 있는 기능들을 소개할게요.

Erlang 모듈은 Elixir와 네이밍 규칙이 달라서 소문자로 시작해요. 두 경우 모두 모듈 이름은 아톰이고, 함수를 호출할 때 모듈 이름으로 디스패치해요.

iex> is_atom(String)
true
iex> String.first("hello")
"h"
iex> is_atom(:binary)
true
iex> :binary.first("hello")
104

Elixir에 능숙해질수록 Erlang의 STDLIB Reference Manual을 더 자세히 파고들고 싶어질 거예요.

출처: Erlang libraries

본문

:binary 모듈

Elixir의 내장 String 모듈은 UTF-8로 인코딩된 바이너리를 다뤄요. 반면 :binary 모듈은 반드시 UTF-8이 아닐 수도 있는 바이너리 데이터를 다룰 때 유용해요.

iex> String.to_charlist("Ø")
[216]
iex> :binary.bin_to_list("Ø")
[195, 152]

위 예시가 두 모듈의 차이를 보여줘요. String 모듈은 유니코드 코드포인트를 돌려주지만, :binary는 원시 데이터 바이트를 다뤄요.

포맷된 텍스트 출력

Elixir에는 C 같은 언어의 printf와 비슷한 함수가 없어요. 다행히 Erlang 표준 라이브러리의 :io.format/2:io_lib.format/2를 사용할 수 있어요. 전자는 터미널 출력으로 포맷하고, 후자는 iolist로 포맷해요. 포맷 지정자는 printf와 다르므로, 자세한 내용은 Erlang 문서를 참고해야 해요.

iex> :io.format("Pi is approximately given by:~10.3f~n", [:math.pi])
Pi is approximately given by:     3.142
:ok
iex> to_string(:io_lib.format("Pi is approximately given by:~10.3f~n", [:math.pi]))
"Pi is approximately given by:     3.142\n"

:crypto 모듈

:crypto 모듈에는 해싱 함수, 디지털 서명, 암호화 등이 들어 있어요.

iex> Base.encode16(:crypto.hash(:sha256, "Elixir"))
"3315715A7A3AD57428298676C5AE465DADA38D951BDFAC9348A8A31E9C7401CB"

:crypto 모듈은 Erlang과 함께 배포되는 :crypto 애플리케이션의 일부예요. 즉 프로젝트 설정에 :crypto 애플리케이션을 추가 애플리케이션으로 목록에 올려야 해요. mix.exs 파일을 편집해서 다음처럼 작성하면 돼요.

def application do
  [extra_applications: [:crypto]]
end

:kernel이나 :stdlib Erlang 애플리케이션에 속하지 않은 모듈은 반드시 mix.exs에 애플리케이션을 명시해야 해요. Erlang 문서에서 사이드바의 Erlang 로고 바로 아래에 해당 모듈의 애플리케이션 이름이 적혀 있으니, 그걸 확인하면 돼요.

:digraph 모듈

:digraph:digraph_utils 모듈에는 정점(vertex)과 간선(edge)으로 이뤄진 유향 그래프를 다루는 함수가 있어요. 그래프를 만든 뒤에, 예를 들어 두 정점 사이의 최단 경로나 그래프 안의 루프를 찾는 알고리즘을 활용할 수 있어요.

세 개의 정점이 주어졌을 때, 처음 정점에서 마지막 정점까지의 최단 경로를 찾아볼게요.

iex> digraph = :digraph.new()
iex> coords = [{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}]
iex> [v0, v1, v2] = (for c <- coords, do: :digraph.add_vertex(digraph, c))
iex> :digraph.add_edge(digraph, v0, v1)
iex> :digraph.add_edge(digraph, v1, v2)
iex> :digraph.get_short_path(digraph, v0, v2)
[{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}]

:digraph의 함수들은 그래프 구조를 in-place로 변경해요. 이게 가능한 이유는 아래에서 설명할 ETS 테이블로 구현되어 있기 때문이에요.

Erlang Term Storage (ETS)

:ets:dets 모듈은 큰 데이터 구조를 각각 메모리디스크에 저장해요. ETS는 튜플을 담는 테이블을 만들 수 있게 해줘요. 기본적으로 ETS 테이블은 protected 상태라서, 오너 프로세스만 테이블에 쓰지만 다른 어떤 프로세스도 읽을 수 있어요. ETS는 테이블을 간단한 데이터베이스, 키-값 저장소, 혹은 캐시 메커니즘으로 쓸 수 있게 해주는 기능이 있어요. :ets 모듈의 함수들은 테이블의 상태를 부수 효과(side-effect)로 변경해요.

iex> table = :ets.new(:ets_test, [])
# Store as tuples with {name, population}
iex> :ets.insert(table, {"China", 1_374_000_000})
iex> :ets.insert(table, {"India", 1_284_000_000})
iex> :ets.insert(table, {"USA", 322_000_000})
iex> :ets.i(table)
<1   > {<<"India">>,1284000000}
<2   > {<<"USA">>,322000000}
<3   > {<<"China">>,1374000000}

:math 모듈

:math 모듈에는 삼각 함수, 지수 함수, 로그 함수를 포함한 일반적인 수학 연산이 들어 있어요.

iex> angle_45_deg = :math.pi() * 45.0 / 180.0
iex> :math.sin(angle_45_deg)
0.7071067811865475
iex> :math.exp(55.0)
7.694785265142018e23
iex> :math.log(7.694785265142018e23)
55.0

:queue 모듈

:queue 모듈은 (양방향) FIFO(선입선출) 큐를 효율적으로 구현한 자료 구조를 제공해요.

iex> q = :queue.new
iex> q = :queue.in("A", q)
iex> q = :queue.in("B", q)
iex> {value, q} = :queue.out(q)
iex> value
{:value, "A"}
iex> {value, q} = :queue.out(q)
iex> value
{:value, "B"}
iex> {value, q} = :queue.out(q)
iex> value
:empty

:rand 모듈

:rand 모듈에는 난수 값을 돌려주고 난수 시드(seed)를 설정하는 함수가 있어요.

iex> :rand.uniform()
0.8175669086010815
iex> _ = :rand.seed(:exs1024, {123, 123_534, 345_345})
iex> :rand.uniform()
0.5820506340260994
iex> :rand.uniform(6)
6

:zip과 :zlib 모듈

:zip 모듈은 디스크나 메모리에서 ZIP 파일을 읽고 쓰는 것은 물론, 파일 정보를 추출하는 것도 가능하게 해줘요. 다음 코드는 ZIP 파일 안의 파일 수를 세는 예시예요.

iex> :zip.foldl(fn _, _, _, acc -> acc + 1 end, 0, :binary.bin_to_list("file.zip"))
{:ok, 633}

:zlib 모듈은 Unix 계열 시스템의 gzip 명령줄 도구에서 볼 수 있는 zlib 포맷의 데이터 압축을 다뤄요.

iex> song = "
...> Mary had a little lamb,
...> His fleece was white as snow,
...> And everywhere that Mary went,
...> The lamb was sure to go."
iex> compressed = :zlib.compress(song)
iex> byte_size(song)
110
iex> byte_size(compressed)
99
iex> :zlib.uncompress(compressed)
"\nMary had a little lamb,\nHis fleece was white as snow,\nAnd everywhere that Mary went,\nThe lamb was sure to go."

Erlang 배우기

Erlang을 더 깊이 배우고 싶다면, Erlang의 기초와 고급 기능을 다루는 온라인 자료들을 소개할게요.

  • Erlang Syntax: A Crash Course는 Erlang 문법을 간결하게 소개해요. 각 코드 스니펫마다 Elixir로 작성한 동등한 코드가 함께 나와 있어서, Erlang 문법을 익히면서 동시에 배운 Elixir를 복습할 기회가 돼요.
  • Erlang 공식 웹사이트에는 짧은 튜토리얼이 있어요. 동시성 프로그래밍을 위한 Erlang의 기본 요소를 그림과 함께 설명하는 장도 있죠.
  • Learn You Some Erlang for Great Good!은 Erlang의 설계 원칙, 표준 라이브러리, 모범 사례 등을 훌륭하게 소개하는 책이에요. 위의 crash course를 읽었다면 주로 문법을 다루는 책의 처음 몇 장은 건너뛰어도 안전해요. The Hitchhiker's Guide to Concurrency 장에 이르면 진짜 재미가 시작돼요.

마지막 단계로, 디버깅할 때 사용할 만한 기존의 Elixir(그리고 Erlang) 라이브러리들을 살펴볼 거예요.

더 알아보기

  • Erlang Syntax: A Crash Course에서 Erlang과 Elixir 문법을 병행해서 익혀 보세요.
  • :ets를 시작으로 이어지는 다음 페이지인 "Debugging" 문서에서 실제 디버깅 라이브러리들을 확인해 보세요.
  • 각 Erlang 모듈의 인자와 반환값은 공식 Erlang STDLIB 문서에서 정확히 확인할 수 있어요.