2021년 2월 16일 11시 50분
아래 내용은 인사이트 출판사의 제안으로 작성 중인 책의 초고입니다. 실제 출판 시에는 내용이 달라질 수 있습니다. 많은 의견 부탁드립니다.
프로그래밍 언어는 문법(syntax)과 의미(semantics)로 구성된다. 한 프로그래밍 언어는 자신만의 문법과 의미를 정의하며, 서로 다른 프로그래밍 언어는 서로 다른 문법과 의미를 가진다. 문법은 코드의 생김새를 결정하고 의미는 코드의 뜻을 결정한다. 예를 들어, “1 + 1은 파이썬 코드이지만 1 +는 파이썬 코드가 아니다”라는 말은 파이썬의 문법에 대한 설명이고, “1 + 1을 계산한 결과는 2이고 1 + 2를 계산한 결과는 3이다”라는 말은 파이썬의 의미에 대한 설명이다. 즉, 프로그래머가 어떤 코드를 작성했을 때 그 코드가 정말로 프로그램을 만들어 내는 코드인지 판별하는 것이 문법이고, 그렇게 만들어진 프로그램을 실행하면 무슨 일이 일어나는지 정하는 것이 의미이다. 프로그래밍을 하려면 코드를 작성할 수 있어야 하고 그 코드를 실행할 수 있어야 하니 문법과 의미는 프로그래밍 언어의 필수 요소인 셈이다.
문법은 구체적 문법(concrete syntax)과 요약 문법(abstract syntax)으로 구분할 수 있다. 구체적 문법은 코드 문자열 그 자체에 대한 규칙이라면, 요약 문법은 코드의 구조에 대한 규칙이다. 언어마다 구체적 문법은 편차가 크지만, 언어가 달라져도 요약 문법은 크게 바뀌지 않는다.
예시를 위해 두 수를 받아 그 둘을 더한 값을 결과로 내는 add라는 이름의 함수를 정의하는 코드를 여러 언어로 작성해 보자. (아직 함수에 대해 설명하지는 않았지만 함수가 무엇인지는 독자들이 모두 알 테니 예시에 함수를 사용하겠다. 함수에 대해서는 조금 뒤에 제대로 이야기할 것이다.)
같은 일을 하는 함수를 정의했지만 언어마다 코드의 생김새가 전혀 다르다. 함수를 정의하기 위해 사용한 키워드가 파이썬에서는 def, 자바스크립트에서는 function, 래킷에서는 define, 오캐멀에서는 let으로 각각 다른 것을 볼 수 있다. 그뿐만이 아니다. 매개변수 이름 n, m을 쓸 때 파이썬과 자바스크립트에서는 괄호 안에 쉼표로 구분해 쓴 반면, 래킷과 오캐멀에는 괄호도 쉼표도 없다. 그 밖에도 자바스크립트에는 중괄호가 있고, 래킷은 덧셈을 할 때 +를 제일 앞에 쓰는 등 여러 차이점이 있다. 지금까지 살펴본 차이점은 모두 구체적 문법의 차이이다. “함수를 정의할 때 파이썬에서는 def라 쓰고 자바스크립트에서는 function이라 쓴다”는 말은 구체적 문법을 비교한 것이다. 예시에서 알 수 있듯이 구체적 문법은 언어에 따라 다르다.
하지만 코드의 구조는 어느 언어에서나 똑같다. 함수를 정의하려면 함수 이름, 매개변수 이름, 몸통(함수의 결괏값을 정하는 부분)을 써야 한다. 위의 예시 모두에서 add라는 함수 이름, n과 m이라는 매개변수 이름, n과 m을 더하는 몸통이 등장한다. 또, 덧셈을 할 때는 어떤 두 값을 더할 것인지 써야 한다. 모든 코드에서 덧셈의 피연산자 n과 m이 등장한다. 코드란 사람의 생각을 표현하는 것이고 어느 언어를 사용하든 사람의 생각에는 큰 변화가 없으니, 언어가 바뀌어도 코드의 구조가 달라지지 않는 것은 당연한 일이다. 요약 문법은 코드의 구조를 설명한다. “파이썬에서는 함수를 정의할 때 함수 이름, 매개변수 이름, 함수 몸통을 써야 한다”는 문장은 파이썬의 요약 문법을 설명한 것이다. 이는 언어를 자바스크립트로 바꾸어도 여전히 말이 된다. “자바스크립트에서는 함수를 정의할 때 함수 이름, 매개변수 이름, 함수 몸통을 써야 한다”는 설명도 올바르니 말이다. 이처럼 언어가 다를지라도 요약 문법은 비슷하다.
앞으로의 모든 설명은 요약 문법만을 고려한다. 이 책의 목표는 여러 정적 언어에 대한 일반적인 설명을 하는 것이다. 구체적 문법은 언어마다 다르지만 요약 문법은 언어에 관계없이 항상 비슷하니 책이 요약 문법만을 사용하는 것이 당연하다. “함수를 정의할 때는 def라고 쓴 다음 함수 이름을 쓰고 괄호를 연 뒤에 ……”라고 설명한다면 그런 구체적 문법을 가진 언어에만 적용될 수 있으니 책의 방향성과 맞지 않다. 반면 “함수를 정의할 때는 함수 이름, 매개변수 이름, 몸통을 쓴다”고 설명하면 이런 요약 문법을 가진 모든 언어에 적용 가능하다.
요약 문법을 사용할 때의 한 가지 문제점은 프로그램의 구조를 매번 말로 설명하는 것이 상당히 장황하다는 것이다. 함수를 정의하는 것을 설명하려면 “함수를 정의할 때는 함수 이름, 매개변수 이름, 함수 몸통을 쓴다”고 써야 하며, 정의한 함수를 설명할 때는 “함수 이름은 add이고 매개변수 이름은 n과 m이며 몸통은 n과 m을 더하는 함수를 정의했다”고 써야 할 것이다. 한두 번이면 몰라도 책 시작부터 끝까지 이렇게 설명하면 너무 알아보기 어렵다.
이런 문제가 없도록 코드와 비슷한 표기법을 통해 요약 문법을 나타낼 것이다. 함수 정의의 요약 문법을 설명할 때 다음과 같이 쓸 것이다.
def x(x, …, x) = e
여기서 x는 임의의 이름, e는 임의의 식이다. def와 괄호, 쉼표, 등호는 이미 존재하는 언어의 구체적 문법과 비슷하게 선택한 표기법일 뿐이다. 다시 말해, def와 같은 요소는 어디까지나 보기 좋으라고 있는 장식으로, 아무런 뜻도 없다. 따라서 위 문장은 함수를 정의할 때 def라는 키워드를 사용한다고 말하는 것이 아니다. 핵심은 함수 정의를 구성하는 요소인 x와 e이다. 함수 정의가 함수 이름, 임의의 개수의 매개변수 이름, 몸통 식으로 구성된다는 것이 위 문장이 설명하는 내용의 전부이다.
많은 경우에 같은 문자로 표시되는 서로 다른 개체를 구분하기 위해 번호를 붙일 것이다. 예를 들면 위에서 본 함수 정의의 요약 문법은 다음처럼 쓰는 것이 더 낫다.
def x(x1, …, xn) = e
이 경우, “x는 함수 이름, x1부터 xn까지는 매개변수 이름이다”라고 명확하게 설명할 수 있다. 비슷한 이유로 작은따옴표를 사용할 수도 있다. 즉, x, x1, x’, x1’이 함께 등장한다면 네 개의 (우연히 같을 수도 있지만) 서로 다른 임의의 이름을 나타낸다.
요약 문법에 따라 만들어진 프로그램의 예시를 보여 줄 때도 코드와 비슷한 표기법을 사용한다. 예를 들면, 함수 정의를 다음과 같이 쓸 것이다.
def add(n, m) = n + m
이는 함수 이름이 add, 매개변수 이름이 n과 m, 몸통이 n과 m 사이의 덧셈이라는 사실을 나타내며 그 이외에는 어떤 뜻도 없다.
책에서 구체적 문법은 다루지 않고 요약 문법만 고려하니, 굳이 번거롭게 계속 “요약 문법”이라고 부를 필요가 없다. 지금부터 “문법”이라고 하면 요약 문법을 뜻한다.