Erlang 프로그래밍 소개, Part 1: 기본 사항
Erlang은 동시성 및 분산 시스템을 개발하는 데 주로 사용되는 다목적 프로그래밍 언어입니다. 처음에는 Ericsson에서 전화 및 통신 애플리케이션을 개발할 때 사용한 사설 프로그래밍 언어였습니다. 1998년에 오픈 소스로 릴리스되었으며 Facebook 채팅 시스템과 같은 유명한 프로젝트와 CouchDB 문서 지향 데이터베이스 관리 시스템과 같은 현실적인 오픈 소스 프로젝트에서 사용된 덕택에 최근에 더 유명해지게 되었습니다. 이 기사에서는 Erlang을 학습하고 Erlang의 함수형 프로그래밍 스타일 및 필수적이고 절차적인 오브젝트 지향 프로그래밍과 같은 다른 프로그래밍 패러다임을 비교합니다. 첫 번째 프로그램인 피보나치 재귀 함수를 작성하는 방법을 배웁니다. 다음에는 C, C++, Java™ 및 Python에 익숙한 개발자라면 처음에 어렵게 느낄 수 있는 Erlang 언어의 기본적인 기능을 살펴봅니다.
Erlang이란?
Erlang은 다양한 전자 통신 프로젝트를 관리할 수 있는 소프트웨어를 쉽게 개발하도록 하기 위해 Ericsson에서 개발했으며, 첫 번째
버전은 1986년에 릴리스되었고 이 언어의 첫 번째 오픈 소스는 1998년에 릴리스되었다. 확장된 Erlang 릴리스 정보에서 이점을 확인할 수
있으며 여기에서는 Erlang용 애플리케이션 개발 플랫폼인 OTP(Open Telecom Platform)가 Erlang 개발 환경을 제공하는
기본적인 방법으로 존재한다.
Erlang은 다른 언어에는 없거나 다른 언어에서는 관리하기 어려운 표준 기능이 많이 있다. Erlang은
전자 통신 분야에서 사용할 목적으로 개발되었기 때문에 Erlang에는 이러한 기능이 대부분 존재한다.
예를 들면, Erlang에는 매우
간단한 동시성 모델이 포함되어 있으며 이 모델을 이용하면 동일한 호스트에서 개별 코드 블록을 비교적 쉽게 여러 번 실행할 수 있다. 이러한
동시성 이외에도 Erlang에서는 이러한 프로세스 내에서 발생하는 실패를 새로운 프로세스를 통해 식별하고 처리할 수 있는 오류 모델을 사용한다.
따라서 개발자는 고장 허용 기능이 우수한 애플리케이션을 매우 쉽게 빌드할 수 있다. 마지막으로 Erlang에는 분산 처리 기능이 내장되어 있어서
이 기능을 이용하면 한 시스템에서는 컴포넌트를 실행하면서 다른 시스템에서는 요청을 받을 수 있다.
종합해서 말하자면 Erlang은 최신
네트워크와 웹 기반 애플리케이션을 지원하기 위해 자주 사용하는 확장 가능한 분산된 고성능 이산 애플리케이션 유형을 빌드하는 데 매우 적합한
환경을 제공한다.
함수형 프로그래밍 Vs. 기타 패러다임
Erlang과 기타 인기 있는 언어 간의 기본적인 차이점은 Erlang은 기본적으로 함수형 프로그래밍 언어라는 점이다. 이는 Erlang의
함수 지원 여부와는 관계가 없고 프로그램과 컴포넌트 조작이 작동하는 방식과 관련이 있다.
함수형 프로그래밍 언어는 입력을 받아서 결과를
생성하는 함수로 조작된다는 점에서 함수형 프로그래밍에서는 해당 언어의 함수와 조작이 수학 계산과 비슷한 방식으로 설계된다. 함수형 프로그래밍
패러다임에서는 동일한 값을 입력하면 개별 코드 블록에서 출력 값이 일관되게 생성된다. 이렇게 되면 함수나 프로그램의 출력을 훨씬 더 쉽게 예측할
수 있어서 코드를 디버깅하거나 분석하기가 더 쉬워진다.
실행 과정에서 애플리케이션의 상태를 변경해야 하는 Perl 또는 Java와 같은
필수 프로그래밍 언어는 프로그래밍 패러다임이 대조적이다. 필수 프로그래밍 언어에서 애플리케이션의 상태가 변경된다는 것은 프로그램의 개별
컴포넌트가 특정 시점의 프로그램 상태를 기반으로 동일한 입력 값에 대해 다양한 결과를 생성한다는 것을 의미한다.
함수형 프로그래밍 방식은
이해하기 쉽지만, 개발자가 더 절차적이고 상태 중심적인 필수 언어에 익숙한 경우에는 적용하기 어려울 수 있다.
Erlang 얻기
Erlang 웹 사이트에서 Erlang을 직접 얻을 수 있다(참고자료 참조). 또한, 많은 Linux 배포판의 저장소에는 Erlang이
포함되어 있다. 예를 들어, Gentoo에 설치하려면 $ emerge dev-lang/erlang을 사용한다. 또는 $ apt-get
install erlang 명령을 사용하여 Ubuntu나 Debian 배포판에 설치할 수도 있다.
기타 UNIX? 및 Linux 플랫폼의
경우에는 소스 코드를 다운로드하여 직접 빌드할 수 있다. 소스를 사용하여 빌드하려면 C 컴파일러와 make 도구가 있어야 한다(참고자료 참조).
기본 단계는 다음과 같다.
1. 다음과 같이 소스를 압축 해제한다. $ tar zxf otp_src_R14B01.tar.gz
2. 해당 디렉토리로 변경한다. $
cd otp_src_R14B
3. 구성 스크립트를 실행한다. $ ./configure
4. 마지막으로 make를 실행하여 빌드한다:
$ make
Windows? 설치 프로그램 역시 Erlang 웹 사이트에서 얻을 수 있다(참고자료 참조).
첫 번째 Erlang 프로그램인 피보나치 재귀 함수
함수형 프로그래밍 스타일의 이점과 이 스타일이 Erlang 안에서 작동하는 방식을 이해하려면 피보나치 함수를 살펴보는 것이 가장 좋다.
피보나치 숫자는 정수 시퀀스이며 여기에서는 다음 공식을 사용하여 특정 숫자의 피보나치 값을 계산할 수 있다: F(n) = F(n-1) +
F(n-2).
첫 번째 값의 결과인 F(0)는 0이고 F(1)의 결과는 1이다. 이후에는 이전의 두 값을 계산하고 그 값을 서로 더하여
F(n)을 계산한다. 예를 들면, F(2)를 계산한 값은 목록 1에 표시되어 있다.
피보나치 수열은 많은 시스템에서 재무 데이터를 분석할 때 사용되는 중요한 계산이자 손쉬운 방법이며, 나무의 가지나 줄기에 잎을 배열하는
기본 원리이기도 하다. 3D로 표현된 나무가 있는 비디오 게임에서는 피보나치 수열을 사용하여 가지와 잎의 배열을 계산하여 가지와 잎의 위치를
결정한다.
프로그래밍 언어로 계산을 프로그래밍할 때는 순환을 사용하여 계산을 완료할 수 있으며 여기서는 해당 함수가 자신을 호출하여 근원,
즉 F(0) 및 F(1)에서부터 숫자를 계산한다.
Erlang에서는 변수와 고정 값이 있는 함수를 작성할 수 있다. 이렇게 하면 피보나치
수열을 단순화할 수 있으며 여기에서는 F(0)과 F(1)이 계산 값 대신 명시 값을 리턴한다.
따라서, 기본 함수는 세 가지 상황으로
구성된다. 첫 번째는 0을 제공했을 때이고 두 번째는 1을 제공했을 때이며 세 번째는 이보다 더 높은 값을 제공했을 때이다. Erlang에서는
이러한 명령문을 세미콜론을 사용하여 분리하므로 기본 피보나치 함수는 목록 2와 같이 정의할 수 있다.
첫 번째 행에서는 fibo(0)을 호출한 결과를 정의하고(-> 기호는 함수의 본문과 정의를 분리함), 두 번째 행에서는 fibo(1)을 호출한 경과를 정의하며 세 번째 행에서는 양수 값 N을 제공했을 때 실행할 계산을 정의한다. Erlang에서는 시스템이 패턴 일치를 호출하기 때문에 이 함수는 작동한다. 이러한 내용은 나중에 자세하게 살펴보게 된다. 마지막 명령문(그리?제 계산은 간단하다. 이제 Erlang 언어의 구조를 더 구체적으로 살펴보도록 하자.
기본 사항
Perl, Python 또는 PHP와 같은 언어에 익숙한 경우에는 Erlang의 구조와 레이아웃이 약간 이상하게 보이겠지만, 여기에는
애플리케이션을 작성하는 전체 프로세스를 더 단순화하는 몇 가지 특성이 있어서 개발자가 코드의 여러 가지 특성에 관해 덜 신경 써도 된다. 특히,
Erlang은 다른 언어에 비해 매우 엉성해 보일 수 있으며 특정 조작, 표현식 및 구성이 단지 한 행으로 표시되는 경우가
많다.
Erlang을 이해하는 가장 쉬운 방법은 Erlang 쉘을 사용해 보는 것이다. Erlang 쉘을 실행하려면 Erlang을 설치한
후에 명령행에서 erl을 실행하면 된다. 목록 3에서 이를 확인할 수 있다.
프롬프트를 사용하여 명령문을 입력할 수 있으며 이 명령문은 마침표로 종료되어야 한다. 이 쉘은 명령문이 완료되었을 때 명령문을 평가한다. 따라서 간단한 합계를 입력하면 목록 4와 같은 결과를 리턴한다.
여기에서는 쉘을 사용하여 몇 가지 다양한 유형과 구문을 살펴볼 것이다.
숫자 유형
Erlang에서는 정수 및 부동 소수점과 같은 기본 데이터 유형과 튜플 및 목록과 같은 더 복잡한 구조를 지원한다.
정수와 대부분의
정수 조작은 다른 언어에서와 동일하다. 목록 5와 같이 숫자 두 개를 서로 더할 수 있다.
그리고 목록 6과 같이 소괄호를 사용하여 계산을 함께 그룹화할 수 있다.
목록 6에서는 명령문을 종료하는 마침표가 별도의 행에서 앞에 있는 계산을 평가한다.
Erlang에서는 부동 소수점을 사용하여 실수를
표현하며 목록 7과 같이 그대로 부동 소수점을 표현할 수 있다.
목록 8과 같이 부동 소수점을 지수를 사용하여 표현할 수도 있다.
정수 값과 부동 소수점 값에 표준 수학 연산자(예:+, -, /, *)를 사용할 수 있으며 계산 과정에서 부동 소수점과 정수를 짝지어 사용할 수 있다. 그러나 계수와 나머지 연산자는 정수 값에만 사용할 수 있기 때문에 부동 소수점 값에 동일한 계수와 나머지 연산자를 사용하면 오류가 발생한다.
원자(Atom)
원자는 정적 또는 상수형 리터럴이다. 목록 9에는 예제가 표시되어 있다.
원자는 C에서 #define 값을 사용하는 것과 같은 방식, 즉 더 분명하게 값을 지정하거나 식별하는 방식으로 사용된다. 따라서 원자를
대상으로 사용할 수 있는 연산자는 비교 연산자뿐이다. 이러한 방식으로 원자를 사용하여 부울 논리로도 확장할 수 있다. 이는 명령문에 대한 부울
결과값을 식별하기 위한 true 및 false 원자의 가용성과도 연계되어 있다. 원자는 소문자로 시작해야 하며 그렇지 않고 작은따옴표를 사용하여
구분할 수도 있다.
예를 들면, 정수를 비교하여 그 결과로 부울 원자를 얻을 수 있다(목록 10).
또는 목록 11과 같이 원자를 비교할 수도 있다.
원자는 사전식으로 정렬된다. 즉 z는 a보다 값이 더 크다(목록 12 참조).
and, or, xor 및 not과 같은 표준 부울 연산자를 사용할 수 있다. is_boolean() 함수를 사용하여, 제공된 값이 true인지 false인지 확인할 수 있다.
튜플(tuple)
튜플은 복합 데이터 유형으로 항목으로 구성된 콜렉션을 저장할 때 사용된다. 튜플은 중괄호를 사용하여 구분한다(목록 13 참조).
튜플의 내용이 모두 동일한 유형일 필요는 없지만 특별한 경우에는 튜플의 첫 번째 값이 원자로 구성된다. 이런 경우에는 첫 번째 원자를 태그라고 하며 이 첫 번째 원자를 사용하여 내용을 식별하거나 분류할 수 있다(목록 14 참조).
여기서 태그는 email이며, 이 태그를 사용하여 튜플의 나머지 내용을 식별할 수 있다.
튜플은 정의된 요소를 포함하고 다양한 복합
데이터 구조를 기술하는 데 매우 유용하다. Erlang을 이용하면 튜플에 있는 값을 명시적으로 설정하고 가져올 수 있다(목록 15
참조).
튜플의 요소는 첫 번째 값이 0 대신 1로 색인화되며 다른 언어에서도 이렇게 색인화하는 것이 일반적이다. 튜플을 전체적으로 비교할 수도 있다(목록 16 참조).
목록
마지막 데이터 유형은 목록이다. 목록은 대괄호로 표시된다. 목록과 튜플은 비슷하다. 그러나 튜플은 비교할 때만 사용되는 반면에 목록을
사용하면 매우 다양한 조작을 수행할 수 있다.
기본 목록은 목록 17과 같은 형태로 되어 있다.
사실상 문자열은 특별한 목록 유형이다. 큰따옴표가 있는 값을 사용하여 문자열 값을 작성할 수 있다 하더라도 Erlang에서는 문자열의 개념을 직접 지원하지는 않는다(목록 18 참조).
그러나 사실상 문자열은 ASCII 정수 값 목록일 뿐이다. 따라서 위에 있는 문자열은 ASCII 문자 값 목록으로 저장된다(목록 19 참조).
간단하게 $Character 표기법을 사용하여 문자를 지정할 수도 있다(목록 20 참조).
문자열이 포함된 목록(문자 목록)은 매우 다양한 방법으로 조작할 수 있다. 이 기사에서는 문자열과 원자 간의 기본적인 차이점을 강조해서
설명할 것이다. 원자는 정적 식별자이지만, 컴포넌트 파트(각 문자)를 조사하여 문자열을 조작할 수 있다. 예를 들면, 원자는 단일 엔티티이기
때문에 원자 안에 있는 개별 단어(예: 'Quick brown fox')를 식별할 수는 없다. 그러나 문자열은 여러 개의 단어(예:
["Quick","brown","fox"])로 분리할 수 있다.
목록을 조작하는 데 필요한 많은 함수가 목록 모듈에서 제공된다. 예를
들면, 정렬 함수를 사용하여 목록에 있는 항목을 정렬할 수 있다. 이 함수는 기본 제공 함수가 아니므로 모듈과 함수 이름을 지정해야 한다(목록
21 참조).
목록 조작
생성자를 사용하여 여러 개의 요소로 구성된 목록을 구성할 수 있다. 이 생성자는 요소와 또 다른 목록을 이용하여 목록을 빌드한다. 다른
언어에서는 이러한 유형의 구성을 push() 함수나 연산자로 처리한다. Erlang에서는 헤드(목록의 시작)와 테일을 식별하기 위해
[Head|Tail] 표기법에서 |(파이프) 연산자를 사용한다. 여기서 헤드는 단일 요소이고 테일은 나머지 목록이다.
목록 22에는 목록의
첫 번째 요소에 값을 새로 추가하는 방법이 표시되어 있다.
목록 23과 같이 이 작업을 전체 목록을 대상으로 반복할 수 있다.
이 예제에서는 비어 있는 목록이 끝에 있는데, 이는 목록을 적절하게 잘 구성했다는 것을 의미한다. 첫 번째 항목은 요소이지, 목록 단편이 아니다. 다른 방식을 병합하면 중첩된 목록을 구성할 수 있다(목록 24 참조).
마지막으로 목록 25와 같이 ++ 연산자를 사용하여 목록을 병합할 수 있다.
그리고 왼쪽에 있는 목록에서 연산자의 오른쪽에 있는 목록의 각 요소를 제거할 수 있다(목록 26 참조).
문자열은 목록이므로 문자열을 대상으로 동일한 작업을 수행할 수 있다(목록 27 참조).
데이터 유형과 관련해서는 이 작업이 가장 고급 기술이지만 다행히도 이러한 작업을 살펴보면 기본적인 유형과 조작을 충분히 이해할 수 있게 된다.
표현식과 패턴 일치
이 기사를 통해 이미 다양한 데이터 유형과 여러 가지 표현식 및 구문을 많이 살펴보았다. 표현식의 한 가지 중요한 요소는 변수이다. Erlang에서는 변수가 대문자로 시작되고 그 다음에 대문자와 소문자 및 밑줄의 조합이 따라온다(목록 28 참조).
Erlang에서는 변수에 값이 일회성으로 바인딩된다. 변수를 바인딩하면 변수의 값을 변경할 수 없다(목록 29 참조).
이점은 변수를 정의하면 변수의 값이 변할 수 있다는 것을 의미하는 대부분의 언어와는 다른 점이다. 따라서 단일 할당을 하는 경우에는 값의 결과를 계산하려면, 이 값을 새로운 변수에 할당해야 한다(목록 30 참조).
이러한 단일 할당을 하게 되면 계산 과정에서 변수 값을 삽입하거나 변수 값을 고의적으로 변경하기가 훨씬 더 어려워지기 때문에 프로그래밍
과정에서 값을 식별하고 디버깅하기가 훨씬 더 수월해진다. 또한, 구조를 단순화할 수 있기 때문에 코드가 더 명확해지고 때로는 더 짧아지기도
한다.
이러한 조작은 값이 변수에 바인드되기 전에 계산된다는 것을 의미한다. 다른 언어에서는 함수나 조작에 대한 참조를 기반으로 값을
설정할 수 있기 때문에 참조를 액세스하는 시점의 참조 값에 따라 값이 변할 수 있다. Erlang에서는 변수 값이 언제나 변수가 작성되는
시점에서 알려진다.
f(Varname) 함수를 사용하여 변수 하나의 바인딩을 명시적으로 무시하거나 f() 함수를 사용하여 모든 변수의
바인딩을 무시할 수 있다.
변수에 값을 할당하는 것은 실제로는 특별한 패턴 일치 유형이다. 또한, Erlang에서는 패턴 일치를 사용하여
개별 명령문의 실행 플로우를 처리하고 복합 데이터 유형(튜플, 배열)에서 개별 값을 추출한다. 따라서 패턴 일치의 기본은 다음과 같다. 패턴 =
표현식
표현식은 데이터 구조, 바인드 변수(즉, 값이 있는 변수), 수학 연산 및 함수 호출로 구성된다. 이 조작의 양 측면은 일치해야
한다, 다시 말해서 패턴이 두 개의 요소로 구성된 튜플인 경우에는 표현식도 두 개의 요소로 구성된 튜플이 되어야 한다. 표현식이 실행될 때
표현식이 평가되고 그 결과가 패턴에 할당된다.
예를 들면, 하나의 패턴 일치를 사용하여 값을 두 개의 변수에 동시에 할당할 수 있다(목록
31 참조).
평가 과정에서 패턴이 변수에 바인드되거나 패턴의 요소가 변수에 바인드되는 경우에는 패턴 일치의 결과가 비교된다. 이렇게 되면 더 강력한
선택적 할당이 가능해진다. 예를 들어, 튜플에서 이름과 이메일을 가져오기 위해 다음과 같은 패턴 일치를 사용할 수 있다. {contact,
Name, Email} = {contact, "Me", "me@example.com"}.
마지막으로 앞서 언급한 구성 표현식을 사용하여
목록이나 튜플에서 요소를 가져오기 위해 패턴 일치를 사용할 수 있다. 예를 들면, 목록 32에는 나머지 요소를 유지하면서 목록에서 첫 번째 두
요소를 가져오는 방법이 표시되어 있다.
A와 B에는 각각 값 1과 2가 할당되고 C에는 목록의 나머지 요소가 할당된다.
함수
다른 언어와 마찬가지로 Erlang에서도 함수가 모든 프로그램의 기본 빌딩 블록이 된다. 함수는 원자로 정의된 함수 이름과 소괄호 안에 있는 0개 이상의 함수 인수(예: sum(N,M) -> N+M)로 구성? 한다. (쉘 안에서부터 함수를 정의할 수는 없다.) 인수에는 더 복잡한 유형이 포함될 수 있다. 예를 들면, 튜플에 있는 태그를 사용하여 다양한 조작을 선택할 수 있다(목록 33 참조).
세미콜론은 각 함수 정의 사이에 존재하는 연산자이다. 패턴 매칭은 함수의 인수를 평가하는 데 사용되므로 요소가 세 개인 튜플을
mathexp() 함수에 제공하는 경우에는 패턴 일치가 실패한다.
또한, Erlang에서는 함수가 서로 다른 수의 인수를 받으며, 유효한
함수 정의를 찾을 때까지 패턴 일치를 평가하여 함수의 올바른 정의를 선택한다. 함수 인수의 수를 함수의 애리티(arity)라고 하며 이 애리티는
함수의 식별을 용이하게 하기 위해 사용된다.
피보나치 예제를 다시 살펴보면, fibo(0) 함수가 호출되었을 때 이 패턴이 첫 번째 함수
정의와 일치하여 값을 리턴했고 fibo(1) 함수가 두 번째 정의와 일치했으며 기타 값은 마지막 정의와 일치했다는 점을 알 수 있다. 또한, 이
예제에서 함수 실행의 순환이 어떻게 작동하는지 알 수 있다. 예를 들면, fibo(9) 함수를 호출하면 fibo(0)과 fibo(1) 함수가
목적을 이루어 고정 값을 리턴할 때까지 fibo(N) 함수가 해당 값으로 호출된다.
모든 함수의 리턴 값은 해당 절에 있는 마지막 표현식의
결과이다. (이 기사의 예제에서는 한 행만 있다.) 변수는 일치가 확인되고 변수가 해당 함수의 로컬 변수인 경우에만 할당된다.
모듈
다른 언어에 있는 모듈과 마찬가지로 모듈은 비슷한 함수를 함께 합칠 때 사용한다.
해당 파일에서 모듈 이름을 지정한 다음(파일 이름이
일치해야 함), 모듈을 로드하는 다른 프로그램으로 내보낼 모듈 안에 함수를 지정한다. 예를 들면, 목록 34에는 fib 모듈의 정의가 포함된
fib.erl 파일이 표시되어 있다.
모듈 스펙은 -module() 행에 있다. -export()에는 내보낼 함수의 목록이 포함되어 있다. 각 함수의 정의에는 파일 이름이
함수의 애리티와 함께 표시되어 있다. 이러한 것을 이용하면 해당 함수의 특정 정의를 내보낼 수 있다.
모듈을 사용하려면 모듈을 컴파일한
후, 로드해야 한다. 목록 35와 같이 c() 명령문을 사용하여 쉘 안에서 이러한 작업을 수행할 수 있다.
fib 모듈 내에서 printfibo() 함수를 호출할 수 있도록 함수 호출에는 해당 모듈의 이름이 포함되어 있다.
결론
Erlang 프로그램의 구조와 형식은 대부분의 다른 언어와 매우 다르다. 데이터 유형과 기본적인 표현식은 대부분 동일하지만, 사용법과
애플리케이션은 약간 다르다. 패턴 일치 시스템을 통해 설명한 다양한 표현식의 평가에 대한 중요성과 일회성 변수를 고려하면 전형적인 언어 환경을
어느 정도 강력하게 확장할 수 있다. 예를 들어, 동일한 함수에 여러 가지 접근 방식을 정의하는 기능과 패턴 일치를 사용하여 재귀 호출을 하는
기능을 이용하면 일부 함수를 단순화할 수 있다.
다음에는 Erlang의 네트워크 기능과 프로세스 및 메시지 구문 분석 기능을 살펴보고
언어의 유연성과 강력한 기능을 파악하는 방법으로서 MochiWeb HTTP 서버를 살펴본다.
'프로그래밍 > Erlang' 카테고리의 다른 글
얼랭(erlang)으로 구현하는 다중서버 기반의 분산처리 플랫폼 (0) | 2013.08.26 |
---|---|
UNIX 개발자 관점에서 Erlang 배우기 (0) | 2013.08.26 |
Erlang 프로그래밍 소개, Part 2: 고급 기능 사용하기 (0) | 2013.08.26 |