씨앗의 특징과 고급 기능 (박석봉)

차례
 
아래의 내용은 마이크로소프트 잡지의 12월호에 실린 '씨앗'에 관련된 기사입니다. '씨앗'을 이해하는 데 도움이 되기 바랍니다.
1993.12.28 박석봉

 
11월호 별책부록에서는 '씨앗'의 명령형언어로서의 일반적인 기능에 대해서 설명했다. 여기서는 '씨앗'의 특징과 다른 언어에는 없는 고급기능들에 대해서 설명한다. 글쓴이는 이 글을 읽는 사람이 최소한 11월호 별책부록을 읽어보았거나, 아니면 C나 Pascal과 같은 언어를 하나 정도는 알고 있다고 생각하고 설명해 나가겠다.
씨앗의 설계에서 가장 주안점을 둔 것이 읽기쉬움과 단원화 프로그램이다. 여기서는 이 두 부분에 대해서 집중적으로 설명하고자 한다. 그리고 이해를 돕기 위해서 필요에 따라 여러가지 영문언어들과 비교해서 설명하겠다. 주로, 독자들이 가장 많이 알고 있을 것이라고 생각되는 C언어와 비교해서 설명하겠다.
 

1. 씨앗은 C와 무슨 관계인가?

씨앗이 11월호의 별책부록으로 소개된 뒤에 많은 사람들이 다음과 같은 질문을 했다.
"씨앗과 C는 무슨 관계인가?"
"도대체, C와 씨앗이 무엇이 다른가?"
와 같은 의문을 가지게 되는 이유는 여러가지가 있을 것이다.
첫째로는, 11월호 별책부록에서는 명령형언어로서의 씨앗의 일반적인 형태와 기능에 대해서 설명했을 뿐, 씨앗의 특징이나 다른 언어와 다른 점을 부각시킨 것은 아니기 때문일 것이다. 둘째로는, 별책부록에 씨앗의 특징을 설명했지만 이 부분을 정확히 이해하지 못했기 때문일 것이다. 셋째로는, 실제로 모양만 보면 C와 비슷한 점이 있기 때문일 것이다. 특히나 연산자(operator)를 거의 같은 것을 사용하고 절차(procedure)나 문장, 구역의 시작과 끝을 '{' 와 '}' 를 사용한다는 점이 가장 큰 이유인 것 같다.
"씨앗이 C와 무엇이 다른가"는 설명이 진행되는 동안에 자연스럽게 드러날 것이므로 여기서는 우선, 하이텔(hitel)에 있는 큰틀(kntl)의 "한글언어 씨앗"란에서, 위의 같은 질문에 대해서 글쓴이가 답변한 내용을 필요한 부분만을 옮겨놓도록 하겠다.
 

[질문] 씨앗은 C하고 무슨 관계입니까?

 
[답변] '씨앗'이 C하고 무슨관계인가 를 질문하셨읍니다. 그리고 질문의 이유로 문법이 거의 C하고 똑같다고 하셨읍니다. '씨앗'은 C와 많은 관계가 있고, 동시에 아무런 관계가 없읍니다. 이것은 말장난이 아닙니다. 관계가 많다는 이유는 다음과 같읍니다.
'씨앗'을 연구하는 과정에서 참고로 한 언어가 6-7 가지 정도 됩니다. Smalltalk, Modular-2, Pascal, C, Ada, C++, Objective-C, EIFFEL, 등등이 그것입니다. 그중에 한가지 언어로 C가 들어 있음으로 관계가 없을 수 없읍니다. C, Pascal, Modular-2 등등은 명령형 언어입니다. 영어로 comparative language라고 합니다. 명령형 언어는 모두 같은 개념에 기초하고 있읍니다. 즉, 명령형 언어의 프로그램은 컴퓨터가 작업할 내용을 기술하는 것입니다. '씨앗'도 명령형 언어입니다. C와 같은 개념에 기초하고 있고, 따라서 언어의 기술형식이 비슷한 것은 너무나 당연합니다.
 
관계가 없다는 이유는 다음과 같읍니다. 언어의 구문을 설계할 때 C의 구문을 보고 만들지 않았읍니다. 한글을 기초로, 어떤 모양이 되면 한글어순과 문장형태와 비슷하면서, 명령형 언어에서 필요한 여러가지 문장을 구성할까를 고민하면서 만들었읍니다. 제가 이렇게 설명을 하면 다음과 같이 말씀하실 것 같읍니다.
"그렇지만 문장 구조가 C와 같지 않는냐?"
어떤 면에서는 그 말이 옳을 수도 있읍니다. 하지만 아닙니다. 명령형 언어에서 많이 쓰이는 참거짓문, 영어로 if-statement를 예로 들어 봅시다. 각 언어에서 이 문장의 모양은 다음과 같읍니다.
 
M-2 : IF x = 0 THEN s := 0 ELSIF x < 0 THEN s := -1 ELSE s := 1 END Ada : if x = 0 then s := 0; elsif x < 0 then s := -1; else s := 1; end if; C : if ( x == 0 ) s = 0; else if ( x < 0 ) s = -1; else s = 1; 씨앗 : 조건 { 가 = 0 참이면 합 := 0. 가 < 0 참이면 합 := -1. 아니면 합 := 1. }
 
 
'씨앗'의 경우에는 다른 방법으로 표현할 수도 있지만 가장 적합한 문장으로 표현했읍니다. 위의 예를 보면 알 수 있겠지만, 각각의 명령형 언어들은 그 언어의 바탕개념이 같음으로 비슷한 문장을 가지고 있읍니다. 그리고 그 구조도 비슷합니다. 그러나, 자세히 보면 각 언어마다 모두 문장의 모양이 다릅니다. 위의 if-statement의 경우를 보면, C는 Modular-2 나 Ada에 있는 elsif 가 없읍니다.
C의 if문 보다는 M-2나 Ada의 if문이 더욱 발전된 형태입니다. '씨앗'의 경우를 보십시요. C의 문장과 비슷합니까? 차라리, Ada나 M-2에 가깝다고 생각되지 않습니까?
'씨앗'의 각 문장들을 다른 언어들과 비교해 보시면 위에서 설명한 것과 비슷한 점들을 발견할 수 있을 겁니다.
다음으로 영어를 한글로 바꾼것과 별로 차이가 없는 것 같다고 하셨읍니다. 영어를 한글로 번역한 것과 한글 구문에 맞게 만들어진 것이 어떻게 다른가 한번 봅시다. C의 if-statement를 번역해 보겠읍니다. 물론 이것이 최선의 번역이 아닐 수는 있읍니다. 그리고 이것을 씨앗으로 나타내 보겠읍니다.
 
C => if ( x < 0 ) y = 5; 번역 => 만약 ( 가 < 0 ) 이면 나 = 5; 씨앗 => 가 < 0 { 참이면 나 := 5. }
 
어떤 형태가 더 우리말과 가깝다고 생각하십니까? 별로 차이가 없읍니다. 그러나 '별로' 만큼 차이가 있읍니다.
말장난이 아닙니다. 그 별로 만큼의 차이를 만들는 일은 매우 힘든 작업이었읍니다. 그리고 그 '별로' 만큼의 차이들이 쌓여서 많이 차이가 생기는 것이라고 저는 생각합니다.

2. 읽기 쉬움

프로그램을 읽기 쉽도록 하는 궁극적인 목적은 바탕글(source code)을 이해하기 쉽도록 하기 위해서이다. 이해하기 쉽도록 하기 위해서는 생각과 표현이 일치해야 한다. 씨앗의 문장 하나하나는, 모두 머릿속으로 생각한 내용을 변형시키지 않고 그대로 표현할 수 있도록 만들어져 있다.

2.1 참거짓문

참거짓문은 하나의 부울식을 계산한 결과가 참이냐 거짓이냐에 따라서 분기(branch)를 일으키는 문장이다. 영문언어에서는 if statement 라고 하고 그 모양은 대개 비슷하다.
 
C : if ( i < 0 ) j = -1; else j = 1; Modular-2 : IF i < 10 THEN j := -1 ELSE j := 1 END
 
한가지 짚고 넘어갈 것은 Pascal, Modular-2 등등의 언어에도 있고, 물론 씨앗에도 있지만, C에는 부울형(boolean type)이 없다. 그대신 C에서는 0은 거짓(false) 0이 아닌 것은 참(true)로 취급한다. 따라서, 정확히 말하면 C의 if statement는 식이 참인가 거짓인가를 검사하는 것이 아니라, 0인가 0이 아닌가를 검사하는 것이다.
 
씨앗에서 참거짓문은 다음의 4가지 형태를 가질수 있다.
 
가 < 0 { 가 > 0 { 참이면 나 := -1. 거짓이면 나 := 1. } } 가 < 0 { 가 > 0 { 참이면 나 := -1. 거짓이면 나 := 1. 거짓이면 나 := 1. 참이면 나 := -1. } }
 
위의 예에서 볼 수 있는 것처럼, 참가지(true part)와 거짓가지(false part)는 순서에 상관이 없으며, 또 하나만 있을 수 있다. 이렇게 여러가지로 표현이 가능하기 때문에 머릿속에서 생각한 그대로를 표현할 수 있다. 예를 들어 C에서 어떤 글자가 알파벳인 가를 확인하는 프로그램을 작성한다면 다음과 같은 모양이 될 것이다.
 
if ( ! isalpha( c ) ) { printf( "It is not alphabet\n" ); }
 
이와 비슷하게, 씨앗으로 어떤 글자가 한글인가를 확인하는 프로그램을 작성한다고 하자. 물론 "참이면"을 사용해서 C와 같은 구조로 다음과 같이 쓸 수도 있다.
 
~ 한글인가( 글 ) { // '~'는 논리부정(NOT) 연산자임 참이면 글줄쓰기( "한글이 아닙니다." ). }
 
그러나, "거짓이면"을 사용하면 다음과 같이 휠씬 명확하게 표현할 수 있다.
 
한글인가( 글 ) { 거짓이면 글줄쓰기( "한글이 아닙니다." ). }
 
위와 같이 "거짓이면"을 써서 나타내는 것이 생각을 그대로 표현하는 것일 것이다.

2.2 조건선택문

참거짓문이 하나의 조건식에 대한 분기를 처리하는 문장이라면, 조건선택문은 여러 개의 조건식에 대한 분기를 처리하는 문장이다. 많은 영문언어들에서는 참거짓문과 조건선택문은 하나로 되어 있다.
예를 들어 Modular-2에서는 다음과 같은 구조를 갖는다.
 
IF i > 0 THEN j := 1 ELSIF i < 0 THEN j := -1 ELSE j := 0 END
 
C언어에는 이러한 문장은 없으며, 이것을 처리하기 위해서는 if statement를 여러 개 연결해서 사용한다. 다음과 같은 형태를 띤다.
 
if ( i > 0 ) j = 1; else if ( i < 0 ) j = -1; else j = 0;
 
씨앗의 조건선택문은 다음과 같은 모양을 갖는다.
 
조건 { 가 > 0 참이면 나 := 1. 가 < 0 참이면 나 := -1. 아니면 나 := 0. }
 
 
참거짓문에서와 마찬가지로 각각의 조건가지(alternative)는 "부울식 참이면" 의 형태뿐 아니라 "부울식 거짓이면" 의 형태도 가질 수 있다. 위의 내용을 다음과 같이 표현할 수도 있다.
 
조건 { 가 <= 0 거짓이면 나 := 1. 가 >= 0 거짓이면 나 := -1. 아니면 나 := 0. }
 
 
물론 "부울식 참이면"과 "부울식 거짓이면"을 섞어서 쓸 수도 있다. 이것은 참거짓문에서 설명한 것과 마찬가지로 머릿속에서 생각한 대로 표현할 수 있도록 하기 위한 것이다.

2.3 값선택문

값선택문은 주어진 식의 값을 여러개의 상수값과 비교해서 일치하는 상수값에 연결된 위치로 분기하는 문장이다. 거의 대부분의 언어에는 이러한 문장이 있다. Pascal에서는 case statement 라는 이름으로 불린다. 다음과 같은 모양을 가진다.
 
case I of 0, 2, 4 : Writeln( 'Even digit' ); 1, 3, 5 : Writeln( 'Odd digit' ); else Wirteln( 'Negative or greater than 5' ); end;
 
C언어에서는 switch statement 라는 이름으로 불리는 데, Pascal에 비하면 저수준(low level)이다. 다음과 같은 모양을 가진다.
 
switch ( i ) { case 0 : case 2 : case 4 : printf( "Even digit\n" ); break; case 1 : case 3 : case 5 : printf( "Odd digit\n" ); break; default : printf( "Negative or greater than 5\n" ); }
 
 
C에서 각 "case 상수값 :" 부분은 단순한 갈피(label)의 기능 밖에는 없다. 예를 들어, 위의 보기에서 'i'의 값이 2이면 제어는 "case 2 :" 위치로 옮겨져서 계속 밑으로 흘러내려가게 된다. 그러다가 첫번째 break를 만나면 switch 밖으로 옮겨지게 된다. 만약 실수로 "case 4 :" 부분의 break를 빼먹으면, 제어는 계속 밑으로 흘러가게 되므로 두개의 printf()가 모두 실행되게 된다. 이것은 C가, Pascal처럼 여러개의 상수를 하나의 선택가지에 연결시키는 기능이 없기 때문이다.
이러한 제어의 흘러내림은 특수한 경우에 아주 편리하게 사용될 때가 있다. 씨앗에서는 Pascal과 C의 두가지 형태를 모두 포함하고 있다. 기본적으로 Pascal의 형태를 취하면서 C의 형태로도 사용할 수 있다.
위의 예를 Pascal형태의 씨앗으로 옮기면 다음과 같다.
 
글 값이 { 0, 2, 4 => 글줄쓰기( "짝수입니다.\줄" ). 1, 3, 5 => 글줄쓰기( "홀수입니다.\줄" ). 그밖 => 글줄쓰기( "음수이거나 5보다 큽니다.\줄" ). }
 
위의 것을 다시 C형태의 씨앗으로 옮기면 다음과 같다.
 
글 값이 { 0 => | 2 => | 4 => 글줄쓰기( "짝수입니다.\줄" ). 1 => | 3 => | 5 => 글줄쓰기( "홀수입니다.\줄" ). 그밖 => 글줄쓰기( "음수이거나 5보다 큽니다.\줄" ). }
 
물론, 위와 같은 형태로 쓰지는 않겠지만, 요점은 비교상수열 앞에 '|'를 붙여주면 제어가 값선택문의 끝으로 옮겨지지 않고 계속 밑으로 흘러내려간다는 점이다.

2.4 되풀이문

같은 문장을 반복하는 것이 되풀이 문이다. 다른 언어들에서는 되풀이를 여러가지 문장으로 표현한다. C를 예로 들어보면 for statement, while statement, do-while statement 등의 여러가지를 사용한다.
씨앗에서는 되풀이에 관련된 모든 문장을 되풀이문 하나로 모두 표현한다. 모든 구성을 갖춘 되풀이문의 구조는 다음과 같다.
 
준비 가 := 1. 합계 := 0. 조절 가 ++. 되풀이 가 <= 100 참인 동안 { 합계 += 가. }
 
위의 보기는 1에서 100까지의 합을 계산하는 것이다. 그리고, 이것은 C에서의 for statement 와 같은 구조를 표현한 것이다. 준비부와 조절부를 없애면 while statement와 같아진다. 무한 되풀이는 다음과 같이 간단하게 표현된다.
 
되풀이 { ... }
 
C 에서는 무한 되풀이를 표현하기 위한 특별한 문장이 없으며
 
for (;;) { ... }
 
또는
 
while ( 1 ) { ... }
 
로 표현한다. C 이외의 언어들도 이와 비슷하다. do-while statement와 같은 구조를 표현하려면 무한되풀이문을 사용하고 조건식을 되풀이의 끝에 두어서, 다음과 같이 표현하면 된다.
 
되풀이 { ... 조건식 { 거짓이면 나간다. } }
 
되풀이를 하나의 문장으로 모두 표현함으로, 머릿속으로 생각한 것을 표현하는 과정이 단순해진다. 즉, for 문을 쓸까 아니면 while 문을 쓸까, 아니면 do-while 문을 쓸까에 대한 고민없이, 일단 되풀이 문을 만든 다음, 그것의 구조를 필요한 형태도 만들어 주면 된다. 그리고, 참거짓문과 마찬가지로 "참인 동안" 뿐만 아니라 "거짓인 동안"도 쓸 수 있다. 화일끝까지 글자를 한자씩 읽는 경우를 생각해 보자.
다음과 같이 표현할 수 있다.
 
글자읽기( 화일, 글 ). 되풀이 끝인가( 화일 ) 거짓인 동안 { ... 글자읽기( 화일, 글 ). }
 
이렇게 표현하는 것이
 
글자읽기( 화일, 글 ). 되풀이 ~끝인가( 화일 ) 참인 동안 { ... 글자읽기( 화일, 글 ). }
 
 
보다는 이해하기 쉽다.

2.5 절차호출

영문언어를 사용해서 프로그램을 작성하면서 언어의 차이때문에, 알면서도 계속 틀리는 경우가 있다. 특히, 절차(procedure) 호출에서 실인자의 순서를 잘 틀린다. C언어에서 글줄(string)을 복사하는 경우를 예로 들어보자. 이 경우 보통 strcpy()를 사용하게 되는 데, 이것의 함수원형(function prototype)은 다음과 같다.
 
char *strcpy( char *str1, char *str2 );
 
이 함수는 str2의 내용을 str1으로 복사한다. 그런데, 우리의 보통 어법은 str1str2로 복사하는 것이기 때문에 자주 실수하게 된다.
씨앗의 기본묶음단원(standard library)에 정의되어 있는 모든 절차는 형식인자의 순서가 철저히 한글어순에 따르고 있다. 즉, 절차호출식을 읽으면 자연스러운 한글문장이 될 수 있도록 형식인자의 순서가 정해져 있다. 또, 씨앗에서는 절차호출에서 각각의 실인자에 대응하는 형식인자의 이름을 줄 수가 있다. 예를 들면 다음과 같다.
 
화일체계->이름바꾸기( 옛이름=> "틀린이름", 새이름=> "맞는이름"). 단말기->커서옮기기( 가로좌표=> 10, 세로좌표=> 20 ).
 
이것은 실인자에 대한 편리한 풀이글의 역할을 할 뿐 아니라, 번역기(compiler)가 형식인자의 이름이 맞는가를 검사해 줌으로 실인자를 제대로 주었는지를 확인하는 기능도 함께 가진다.

2.6 인자전달

씨앗에서는 인자전달을 위해서 값전달방식(call-by-value)과 가리킴전달방식(call-by-reference)의 2가지를 제공한다. 물론, Pascal이나 Modular-2에서는 2가지 전달방식이 모두 있다. 그러나 C에는 값전달방식만이 있을 뿐 가리킴전달방식은 없다. C++에는 가리킴전달방식이 추가되었지만, C의 기본라이브러리(standard library)는 가리킴전달방식이 없을 때 만들어진 것이기 때문에 여전히 값전달방식이 주요한 전달방식으로 사용되고 있다.
그렇기 때문에 예를 들어 정수읽기를 하려면
 
scanf( "%d", &i);
 
와 같이 써 주어야 한다. 이것보다는 가리킴전달방식을 사용해서 '&' 없이
 
정수읽기( 값 ).
 
이라고 쓰는 것이 이해하기 편리하지 않겠는가?

2.7 나감문

나감문은 제어를 구역의 안쪽에서 그 바깥쪽으로 옮긴다. C의 break statement가 이에 해당된다. 씨앗의 나감문은 C의 그것보다는 한단계 발전된 기능을 가지고 있다. C의 break statement는 가장 안쪽(innermost)의 순환(loop)만을 빠져나갈 수 있지만, 씨앗에서는 여러겹의 구역을 한번에 빠져나갈 수 있다.
예를 들면 다음과 같다.
 
글자찾기 구역 준비 행 := 0. 조절 행 ++. 되풀이 행 < 행수 참인 동안 { 준비 열 := 0. 조절 열 ++. 되풀이 열 < 열수 참인 동안 { 배열[행][열] = 찾는값 { 참이면 글자찾기 구역에서 나간다. // 두겹을 빠져나감 } } } // 제어는 여기로 옮겨짐
 
위의 보기에서는 나감문을 사용해서 두겹의 되풀이 구역을 한번에 빠져나온 것이다. 이것을 C로 표현하려면 goto statement를 사용하거나, 또는 표시기(flag)를 사용해서 두번 break statement를 써야 한다.

2.8 제어옮김문

많이 쓰이지 않고, 많이 쓰는 것이 좋지는 않지만 경우에 따라서 꼭 필요한 문장이 제어옮김문(goto statement)이다. 씨앗에서 제어옮김문은 2가지 종류가 있다. 하나는 갈피(label)가 붙은 위치로 제어를 옮기는 것이며, 다른 하나는 구역의 시작위치로 제어를 옮기는 것이다. 나감문 단락에서 설명한 대로 씨앗에서는 구역에 이름을 붙일 수 있다. 그리고 이름이 붙은 구역에 대해서 제어옮김문을 사용해서 그 시작위치로 제어를 옮길 수 있다. 갈피로 제어를 옮기는 제어옮김문의 모양은 다음과 같다.
 
<<여기>> ... 여기 갈피로 간다.
 
구역의 처음위치로 제어를 옮기는 제어옮김문의 모양은 다음과 같다.
 
글자찾기 구역 { ... } ... 글자찾기 구역으로 간다.
 

3. 단원

단원은 씨앗을 C와 구별짓는 가장 중요한 특징이다. 씨앗의 단원은 Turbo-Pascal의 unit이나 Modular-2의 module과 비슷하지만 그 보다는 휠씬 발전된 형태이며, 더욱 강력한 기능을 제공한다.
대부분의 경우 프로그램을 작성할 때, 이미 만들어져 있는 프로그램을 사용해서 작업을 한다. C를 예로 들면, 화면에 어떤 메시지를 출력하기 위해서 도스 인트럽트를 직접 사용하기 보다는 기본라이브러리(standard library)에 들어 있는 printf()를 사용하는 것이 일반적이다. 이와 같이 이미 만들어진 프로그램을 사용할 때, 사용자는 그것이 어떻게 구현되어 있는지, 즉, 내부적으로 어떻게 동작하는 지에는 관심이 없거나 관심을 두지 않고, 그것을 어떻게 사용하는 지에 촛점을 맞춘다. 보통, 하나의 라이브러리를 그 내부를 알 수 없는 검은 상자 (black box)라고 생각하는 것이다.
프로그램을 만들기 위해서, 제일 밑바닥부터 필요한 모든 라이브러리들을 만드는 경우는 별로 없으며, 또 바람직하지도 않다. 오늘날, 갈수록 프로그램이 커져가고 복잡해지는 상황에서는 밑바닥에서 부터 필요한 모든 프로그램을 작성한다는 것이 현실적으로 불가능한 경우도 많다. 따라서, 이미 있는 라이브러리를 재사용하는 것은 프로그램 작성자의 생산성에 있어서 갈수록 중요한 요소로 부각되고 있다.
좋은 라이브러리를 만들기 위해서는 무엇보다도 설계가 중요하다. 사용자가 쓰기에 편리하고, 알아야 할 내용이 적고, 필요한 모든 기능이 제공될 수 있도록 라이브러리를 설계하는 것은 좋은 라이브러리를 만들기 위해서 가장 중요한 것이다. 이와 더불어 중요한 것은 정보은폐(information hiding)이다. 이것은 사용자가 라이브러리를 사용하는 데 필요한 최소한의 정보만을 알 수 있도록 하고, 다른 정보들은 사용자가 알 수 없도록 하는 것이다.
단원은 상수, 자료형, 변수, 절차 등을 포함하는 씨앗 프로그램의 기본 단위이다. 단원은 그 자체가 라이브러리로 사용될 수 있을 뿐아니라, 만들어진 라이브러리를 쉽게 사용할 수 있는 구조로 되어 있다. 씨앗은 만들어진 단원을 쉽고 편리하게 재사용할 수 있도록 하며, 동시에 재사용 가능한 단원을 쉽게 만들 수 있도록 여러가지 기능을 제공한다. 그뿐 아니라, 질좋은 단원을 만들 수 있도록 다양한 정보은폐기능도 또한 가지고 있다.
 

3.1 단원의 구조

C에서는 화일의 구조에 대해서 아무런 제한이 없다. 예를 들면 화일에서 변수나 함수가 어떤 위치에 선언되어야 하는 가에 대한 규칙이 없다. 변수나 함수가 외부에서 접근가능한가 하지 않는가는 변수나 함수의 선언 앞에 static을 붙여주는 것을 통해서 조절한다. 씨앗에서 하나의 단원은 하나의 화일로 이루어지며, 단원은 정형화된 구조를 가지고 있다.
 
3.1.1 단원의 구조
단원은 크게 접속부와 구현부로 나누어진다. 접속부는 이 단원이 어떠한 기능을 제공할 수 있는가를 써놓은 부분이다. 구현부는 이러한 기능이 실제로 어떻게 구현되는 가를 쓰는 부분이다. 다시 말하면, 접속부는 단원을 사용하기 위해서 필요한 정보가 기록되는 곳이며 구현부는 실제의 프로그램 코드(program code)가 들어가는 부분이다.
정확히 들어맞는 비교는 아니지만 C로 말하면, 접속부는 헤더화일(header file)에 해당되며, 구현부는 바탕글 화일(source file)에 해당된다. 그러나, 이것은 이해를 돕기위해서 비교한 것일 뿐, 실제로 C는 단원화 프로그램을 목적으로 만들어진 언어는 아니다. 단원의 기본구조는 Turbo-Pascal의 unit나 Modular-2의 separate module과 거의 비슷하다.
정수스택을 단원으로 나타내면 다음과 같다. 물론 다르게 나타낼 수 있는 방식은 여러가지가 있으며, 특히 동적으로 정수스택이 만들어질 수 있도록 하는 것도 좋은 방법이다.
 
정수스택 단원. --+ | 절차 시작점(). | 접속부 절차 넣기( 정수 값 ) => 부울. | 절차 빼기( &정수 값 ) => 부울. | --+ 구현부 --+ | 상수 최대항목수 = 15. | 구현부 정수[최대항목수] 값배열. | 정수 갯수. | | 절차 시작점() | { | 갯수 := 0. | } | | 절차 넣기( 정수 값 ) => 부울 | { | 갯수 < 최대항목수 { | 참이면 | 값배열[갯수++] := 값. | 참 넘긴다. | } | 거짓 넘긴다. | } | | 절차 빼기( &정수 값 ) => 부울 | { | 갯수 = 0 { | 거짓이면 | 값 := 값배열[--갯수]. | 참 넘긴다. | } | 거짓 넘긴다. | }
 
접속부는 단원이 어떠한 기능을 제공하는 가를 선언하는 부분이다. 접속부는 단원에서 외부로 공개된 부분으로서 접속부에 선언된 모든 대상물(object)은 외부에서 접근이 가능한 이름이다. 즉, 단원의 기능을 사용하는 것은 접속부에 선언된 절차나 변수를 통해서 이루어진다. 구현부는 그러한 기능이 어떻게 구현되어 있는 가를 정의하는 부분이다. 여기에는 기본적으로, 구현부에 선언된 절차들이 정의되며, 동시에 구현을 위해서 필요한 상수나 자료형, 변수, 절차를 선언하고 정의할 수 있다. 구현부에서 새롭게 선언된 모든 대상물(object)은 단원의 외부에서 접근이 불가능하다. C처럼 static으로 선언해 주지 않아도 구현부의 모든 것은 단원 내부에서만 의미를 가진다.
 
3.1.2 같은 구조와 자격
씨앗에서 모든 단원은 모두 같은 구조를 가지고 있다. Turbo-Pascal의 경우에는 Program과 Unit을 구별하고, Modular-2에서는 Program module, Definition module, Implementation module의 구별이 있지만 씨앗에서는 모든 단원의 구조가 같다. 모든 단원은 아무런 바탕글의 변화없이 실행화일을 만드는 뿌리가 될 수 있을 뿐아니라, 다른 단원의 하급단원으로써 라이브러리 형태로 사용될 수도 있다.
 
3.1.3 단원의 번역
하나의 단원을 번역하면 접속정보화일과 목적화일(object file)이 만들어진다. Turbo-Pascal의 경우에는 unit을 번역하면 하나의 .tpu 화일이 만들어지지만, 씨앗에서는 2개의 화일이 만들어진다. 목적화일은 일반 .obj 화일과 같은 것이다. 접속정보화일은 단원의 접속부의 내용이 이미 번역이 끝난 상태로 암호화 되어 들어 있다. 이것은 최근의 C++ 컴파일러에서 제공되는 pre-compiled header와 비슷한 형태이다. 하지만 C++에서는 이것을 사용자가 관리해야 하지만, 씨앗에서는 번역기가 항상 최신의 정보가 들어 있도록 관리한다.

3.2 프로그램의 재사용

앞에서 씨앗을 사용하면 프로그램의 재사용이 편리해진다고 주장했다. 그러면 여기서는 실용적인 측면에서 무엇이 어떻게 편리해지는지 살펴보자.
 
3.2.1 큰 프로그램의 작성
C로 여러개의 화일로 이루어진 프로그램을 작업한다고 생각해보자. 먼저, 각 화일을 만들고 각각을 번역(compile)한다. 그 다음 만들어진 각각의 목적화일(object file)을 연결(link)해서 실행화일을 만든다. 프로그램은 한번에 완성되는 것이 아님으로 이러한 작업은 반복적으로 일어난다. 예를 들어 하나의 화일을 고쳤으면, 고친 화일을 다시 번역하고, 또 고친 화일에 영향을 받는 화일들을 다시 번역해야 한다. 이러한 작업은 사람이 하기에는 힘들고 어려운 일임으로, 이것을 자동화하기 위해, 보통 make 를 사용한다. 그러나, 프로그램이 커져서 화일이 상당히 많고 또, 이들간의 관계가 복잡해지면, make를 사용하기 위해서 필요한 makefile을 만들는 일이 힘들고도 어려운 일이 된다. 그래서 또다시 이것을 자동화하기 위해, makefile을 만드는 도구가 다시 만들어졌다.
씨앗에서는 이러한 일이 간단하게 이루어진다. makefile을 전혀 만들 필요가 없으며, 번역과정에서 자동적으로 모든 것이 처리된다. 단원접속부에서 이 단원은 다음과 같은 단원을 사용한다고 써주기만 하면 번역할 때, 번역을 다시 할 필요가 있는가를 검사하는 의존성검사(dependency check)가 자동적으로 이루어지며, 연결(link)도 자동적으로 이루어진다. makefile을 만들 필요가 전혀없다. 만약 계산기라는 단원을 만드는 데 정수에 대한 스택이 필요하다고 하자. 그리고, 위에서 만든 정수스택 단원이 이러한 목적에 사용될 수 있다고 하자. 이러한 경우 필요한 작업은 계산기 단원의 사용선언부에서 정수스택을 사용한다고 선언해 주기만 하면된다.
예를 들면 다음과 같다.
 
계산기 단원. 사용 정수스택. ...
 
그러면 계산기라는 프로그램을 만드는 데 필요한 나머지 모든 작업은 번역기가 자동으로 처리한다.
 
3.2.2 손쉬운 재사용
이미 만들어진 프로그램을 재사용하기위해서 바탕글을 바꾸어야 하는 일이 거의 없다. 이미 있던 프로그램을 재사용하기 위해서 필요한 일은, 단지 재사용될 단원을 사용할 단원의 사용선언부에서 사용한다고 선언해 주기만 하면 된다. C에서 프로그램을 재사용하기 어렵게 만드는 것은 몇가지가 있다. 첫번째로는 재사용을 위해서 바탕글(source)을 고쳐야 한다는 것이다. 물론 그렇지 않을 경우도 있지만, 자신이 써놓은 바탕글과 재사용할 프로그램에 같은 이름이 있으면 바탕글 자체를 고쳐야 한다. 두번째는 앞에서도 지적한 것처럼, 제대로 연결(link)될 수 있도록 makefile을 고쳐야 한다는 것이다.
씨앗에서는 이러한 문제는 전혀 없다. 모든 변수, 자료형, 상수 등의 대상물(object)의 이름은 "단원이름+대상물이름"으로 구성됨으로 단원이름이 같은 경우를 제외하고는 이름의 충돌이 생길 수 없다. 또, 앞에서 여러번 설명한 대로 단순히 사용선언만 해주면 나머지는 자동적으로 이루어진다.
 
3.2.3 시작점과 마무리
시작점 절차와 마무리 절차는 단원의 재사용을 편리하게 할 뿐아니라, 각 단원에 한 차원 높은 독립성을 제공한다. 일반적인 C 라이브러리를 예로 들어서, 시작점 절차와 마무리 절차가 어떤 역할을 하는 지 살펴보자. 대부분의 라이브러리는 라이브러리를 사용할 수 있도록 하기위한 준비절차가 필요하다. 그리고, 사용이 끝난 다음에는 마무리 작업을 해 주어야 한다. C용의 데이타베이스 라이브러리를 예로 들면, 이것을 사용하기 위해서는, 보통 가장 먼저
 
InitSystem();
 
을 해주어야 하고 사용이 모든 끝난 다음에는
 
CloseSystem();
 
 
을 해주어야 한다. 그러나, 가만히 생각해 보면 이러한 작업은 라이브러리가 동작하기 위해서 필요한 것이지, 사용자가 라이브러리를 써서 작업하는 것과는 아무런 관계가 없다. 이러한 작업이 자동적으로 이루어진다면, 사용자가 신경써야 할 것이 하나 줄어들게 될것이며, 사용자가 외워야 할 내용도 하나 줄어들게 될 것이다. 그리고 이러한 간편함이 프로그램을 재사용하는 것을 더욱 편리하게 해 줄 것이다. 시작점 절차와 마무리 절차는 바로 이러한 작업을 처리하는 절차이다. 시작점 절차는 자신의 단원에게 맡겨진 일을 처리하는 데 필요한 준비작업을 하며, 마무리 절차는 작업이 끝난 후의 마무리 작업을 처리한다. 다시 말하면 시작점 절차를 선언하고 준비작업을 처리하도록 정의하고, 마무리 절차를 선언하고 마무리 작업을 처리하도록 정의하면, 그 단원을 사용하는 데 필요한 준비작업과 마무리 작업이 자동으로 처리된다. 이것은 C++에서 Class를 정의할 때 사용하는 constructor, destructor와 비슷하다.
위의 보기에 있는 정수스택을 사용하는 사용자는 정수스택을 사용하기 위해서 [갯수]의 값을 0으로 만들어주지 않아도 된다. 정수스택을 사용한다고 선언해주기만 하면 자동으로 정수스택 단원의 시작점() 절차가 호출되기 때문이다.
 
3.2.4 최소화된 재번역
씨앗에서는 프로그램이 고쳐졌을 경우에 사용자가 번역기에게 바탕글(source file)을 번역하게 하면, 번역기는 필요한 최소한의 바탕글만을 다시 번역한다. 꼭 필요한 것만을 다시 번역하기 때문에 전체 프로그램을 다시 만드는 시간이 최소화된다. 위의 계산기를 예로 들어보자. 계산기를 만드는 과정에서 정수스택에 값이 15개로는 모자라는 경우가 발생했다고 하자. 그래서 최대상수값을
 
상수 최대상수값 = 50.
 
으로 바꾸었다고 하자. 이 경우에 사용자가 전체 프로그램을 다시 만들게 되면, 단지 정수스택 단원만이 다시 번역되고 바로 실행화일이 만들어진다. 프로그램의 변화가 정수스택 안에서만 일어났기 때문에 그것만 번역되는 것이다. 물론, C에서도 전체 프로그램의 구조를 잘 잡고, makefile을 잘 만들면 이러한 일이 불가능한 것은 아니지만, 프로그램이 복잡해 질수록 어려운 일이다.

3.3 정보은폐

앞에서 질좋은 라이브러리를 만들기 위해서는 정보은폐가 중요하다고 설명했다. 단원이 자신을 사용하는 데 필요한 최소한의 정보만을 공개하고, 구현에 관련된 내용을 감추면 감출수록, 단원을 재사용하기가 편리해 진다. 접속부의 내용이 바뀌지만 않는 다면 구현부를 새로운 자료구조나 알고리즘으로 완전히 다시 작성하더라도, 이 단원을 사용하는 단원은 전혀 바꾸지 않아도 된다. 예를 들어 위의 정수스택을 크기가 변할 수 있도록 붙박이 변수로 [값배열]을 잡지 않고 동적으로 기억도막을 배정받아서 사용하더라도 계산기 단원은 아무런 변화가 필요하지 않다. 씨앗에서는 정보은폐를 위해서 여러가지 기능을 제공한다. 단원구조를 설명하면서 선언된 위치에 따라 외부단원에서 볼 수 있고 없고를 제어할 수 있음을 설명했다. 그 이외에도 다음과 같은 기능이 있다.
 
3.3.1 보호된 변수
보호된 변수는 외부로 공개된 변수이기는 하지만, 그 값을 읽을 수만 있을 뿐, 단원의 바깥에서는 그 값을 바꿀 수 없는 변수이다. 물론, 단원 안에서는 값을 바꾸는 것이 가능하다. 보통 어떤 변수를 보호하기 위한 방법으로는, 그 변수를 공개하지 않고 그 변수의 값을 바꾸거나, 값을 읽어오는 절차(procedure)만을 제공하는 방법을 사용한다. 그리고 변수를 보호하는 대부분의 이유는 실수나 잘못 때문에 그 값이 바뀌지 않도록 하려는 것이다. 이렇게 읽기는 허용되지만, 쓰기는 제한되어 있는 변수를 관리하려면 그 값을 읽는 절차를 제공하기 보다는 보호된 변수로 선언하는 것이 편리하고 효율적이다.
 
3.3.2 접근권한의 제어
구조물형에 대해서 각 자료칸(field)에 대한 접근권한을 제어할 수 있다. 이것은 C++에서 structure나 class의 member에 대해서 private이나 public으로 접근권한을 제어하는 것과 같다.
다음을 보자.
 
가 단원. 자료형 권한제어 = 구조물 { 가무림 정수 안보임. 터놓음 정수 보임. }
나 단원. 사용 가. 권한제어 이것. 절차 시작점() { 이것->안보임 := 1. // (1) 이것->보임 := 1. // (2) }
 
위의 예에서 (1)의 문장은 잘못이며 (2)의 문장은 옳은 문장이다. 이와 같이 접근권한을 지정해주면 변수를 선언할 수는 있지만, 그 변수의 자료칸을 접근하지 못하게 할 수 있다. 가무림터놓음을 통해서 단원이 내부적으로 어떻게 구현되었는지를 감출 수 있다.

4. 맺음말

이상으로 간단하게 씨앗이 C와 무엇이 다른가를 중심으로 맞추어서 씨앗의 특징에 대해서 설명했다. C는 물론 좋은 언어이다. C를 설명하는 수많은 책이 있고, C에 대한 수많은 컴파일러와 좋은 개발도구들이 있으며, 또 C를 지원하는 수많은 라이브러리가 있다. 무엇보다도 C가 좋은 언어인 이유는 많은 사람들이 쓰고 있기 때문이다. 여기서는 씨앗의 특징과 장점만을 설명했지만 부족한 부분도 물론 많이 있다. 많은 사람들의 관심과 사랑이 양분과 햇볕이 될 때, 씨앗은 씨앗으로 머물지 않고 화려한 꽃으로 피어날 수 있을 것이다.
씨앗에 대한 의문이나, 좋은 의견이 있으신 분들은 하이텔(hitel) 큰틀에 있는 씨앗란을 이용해 주기 바란다.

 
👉 다음 글:
C에서 씨앗으로