3 min read

Erlang의 iolist

Erlang의 iolist
Photo by Max / Unsplash

iolist는 다음을 요소로 가지는 리스트 type이다.

  • 0부터 255 사이의 값을 가지는 정수 (즉, 1바이트 크기 정수)
  • binary
  • iolist

다음은 iolist의 한 예이다.

1> String = [$H, $e, $l, $l, $o, 32, ["Wor", [<<"ld">>]]].
[72,101,108,108,111,32,["Wor",[<<"ld">>]]]
  • $H는 ASCII 문자이며 0..255 사이의 범위에 속하는 정수로 나타낼 수 있다.
  • 32도 0..255 범위의 정수이다 (ASCII 문자로는 space에 해당한다).
  • "Wor"는 문자열로, Erlang에서 문자열은 ASCII 문자의 리스트이고 결국 정수의 리스트이다.
  • <<"ld">>는 binary이다.
  • [<<"ld">>]["Wor", [<<"ld">>]] 또한 iolist의 범주에 포함된다.

iolist의 장점은 실제로 Screen I/O나 socket I/O 함수를 통해 출력할 때 있다. 보낼 데이터가 들어있는 리스트가 그 안에 또 다른 리스트를 가지고 있는 등 복잡한 구조로 구성되어 있으면 I/O 함수에 보내기 전에 한번 flatten시켜서 보내는게 일반적인데, erlang I/O 함수는 내부에서 iolist type의 리스트를 flatten 해준다. 따라서 위의 String 변수의 리스트를 io:format/2으로 그대로 보내도 동작한다.

2> io:format("~s~n", [String]).
Hello World
ok

이렇게 편리한 함수이지만 모든 값을 받을 수 있는 것이 아니다. 앞에서 설명했듯이, iolist 리스트의 각 요소는 '0부터 255 사이의 값을 가지는 정수'이기 때문에 다음은 오류로 처리된다.

1. atom() type

3> AtomList = [hello, world].
[hello,world]
4> io:format("~s~n", [AtomList]).
** exception error: bad argument
     in function  io:format/3
        called as io:format(<0.26.0>,"~s~n",[[hello,world]])

atom은 심볼처럼 다루기 때문에 erlang 내부로는 복잡한 구조체의 형태로 다룬다. 따라서 atom_to_binary/2 또는 atom_to_list/1 같은 변환 함수를 사용해서 문자열로 바꾸어야 한다.

2. 255보다 큰 정수

5> BigIntList = [1048576].
[1048576]
6> io:format("~s~n", [BigIntList]). 
** exception error: bad argument
     in function  io:format/3
        called as io:format(<0.26.0>,"~s~n",[[1048576]])

특히 기존 C/C++에 익숙한 프로그래머라면 255보다 큰 정수의 경우 2개, 4개 이상의 바이트를 차지할 것이고 그 값을 구성하는 각 바이트가 개별적으로 처리될 것이라고 추측하기 쉽다. 그래서 big endian이냐 little endian이냐만 고민할 것인데, elrang에서는 에러로 처리해버린다.

따라서 직접 정수의 리스트로 쪼개야 하며, 그 중 한 방법은 binary:encode_unsigned/1binary:encode_unsigned/2를 사용하는 것이다.

7> binary:encode_unsigned(1048576).
<<16,0,0>>
8> binary:encode_unsigned(1048576, little).
<<0,0,16>>
— END OF POST.