2020년 10월 17일 16시 52분
아래 내용은 인사이트 출판사의 제안으로 작성 중인 책의 초고입니다. 실제 출판 시에는 내용이 달라질 수 있습니다. 많은 의견 부탁드립니다.
언어에 변수를 추가하기 위해 먼저 해야 할 일은 프로그래머가 변수를 정의하는 방법을 언어가 제공하도록 하는 것이다. 앞서 본 파이썬 프로그램은 다음과 같은 코드를 사용해서 squareOf3
이라는 이름의 변수를 정의했다.
그리고 변수가 정의된 다음에 변수가 사용되는 코드가 위치했다. 다음 코드가 squareOf3
이라는 이름의 변수를 사용하는 식이다.
이 예시로부터 변수를 정의하는 일반적인 방법을 알아낼 수 있다. 파이썬 예시이기는 하나 많은 언어에서 거의 똑같은 방법을 사용한다.
변수를 정의한다는 것은 세 가지를 정하는 것이다. 변수의 이름, 변수의 값, 변수를 사용할 프로그램을 정해야 한다. 우선, 변수를 사용하려면 그 변수를 부를 방법이 필요하니 변수에 이름을 붙여야 한다. 위의 예시에서는 squareOf3
이 정의한 변수의 이름이다. 또, 값에 이름을 붙여서 같은 값을 쉽게 여러 번 사용하려고 변수를 정의하는 것이니 변수의 값을 정해 주어야 함은 당연하다. 이때 변수의 값을 그냥 정수로만 정할 수 있게 하는 것은 너무 제한적이다. 그보다는 어떤 임의의 프로그램을 계산한 결과를 변수의 값으로 사용할 수 있게 하는 것이 프로그래머에게 더 편리한 결정이다. 따라서 변수를 정의할 때 그 변수의 값을 결정하는 프로그램이 있어야 한다. 위의 예시에서는 3 * 3
이 바로 그 프로그램이다. 마지막으로, 변수를 사용하는 프로그램이 필요하다. 변수를 기껏 정의해 놓고 사용하지 않는다면 변수를 정의한 이유가 없다. 당연히 변수를 사용하는 프로그램이 있어야 한다. 예시에서는 print(squareOf3 + squareOf3)
이 정의한 변수를 사용하는 프로그램이다. 정리해 보면, 변수를 정의한다는 것은 변수의 이름, 변수의 값을 정할 프로그램, 변수를 사용할 프로그램을 정하는 것이다.
지금까지는 2장에서 하나의 산술식을 하나의 산술
프로그램이라고 말한 것에 따라 계속해서 3 * 3
, print(squareOf3 + squareOf3)
따위를 프로그램이라 부르고 있다. 이는 여전히 유효하며, 3 * 3
이나 print(squareOf3 + squareOf3)
은 실제로 그 자신만 있어도 파이썬 프로그램이 맞으니 프로그램이라고 불러도 맞는 말이다. 그러나, 어떤 프로그램의 일부분을 떼어 낸 다음 그 부분만을 굳이 따로 프로그램이라고 부르는 경우는 잘 없다. 아무리 프로그램이 맞더라도 프로그램의 일부분을 또 프로그램이라고 부르는 것은 약간 어색하게 느껴진다.
지금부터는 프로그램이라는 단어 대신 ‘식’이라는 단어를 사용하겠다. 예를 들면, 3 * 3
은 하나의 식이며 다시 각각의 3
역시 식인 것이다. 식이라고 부르는 것이 프로그래밍 언어 분야의 일반적인 용어 사용법과도 일치하고, 조금 더 자연스럽게 느껴진다. 하나의 식은 0개 이상의 식으로 구성된다. 이때 어떤 식을 구성하는 식들을 부분식(subexpression)이라 부른다. 3 * 3
은 두 개의 부분식으로 구성된 식으로, 두 부분식은 각각 3
이라는 식이다. 물론 여전히 각각의 식이 하나의 완전한 프로그램이라는 사실을 잊어서는 안 된다. 즉, 식과 프로그램은 이 책에서 거의 동의어로 사용된다. 우리가 정의하는 산술
, 산술x
등에 대해 이야기할 때는 주로 ‘식’이라는 단어를 쓸 것이고, 더 일반적이고 실용적인 맥락에서 이야기할 때는 주로 ‘프로그램’이라는 단어를 쓸 것이다.
이제 식이라는 용어를 사용해서 변수를 정의하는 방법에 대해 다시 써 보자. 변수를 정의하는 식은 하나의 변수 이름과 두 개의 부분식으로 이루어진다. 주어진 변수 이름은 정의하는 변수의 이름을 정한다. 두 부분식 중 하나는 정의하는 변수의 값을 결정하며, 나머지 부분식 하나는 정의한 변수를 사용할 수 있는 식이다. 사실, 앞에서는 변수를 사용하는 프로그램이라고 했다. 변수를 굳이 정의했다면 사용해 주는 것이 일반적이지만, 정의한 변수를 전혀 사용하지 않는 프로그램을 만들 수도 있기는 하다. 따라서 정의할 변수를 ‘사용할 수 있는’ 식이 더 정확한 표현이다.
지금까지 변수의 정의 방법에 대해 이야기한 것을 바탕으로 산술x
의 요약 문법을 정의해 보겠다. 우선, 산술x
는 산술
의 확장인 만큼, 산술
에 있던 기능들을 그대로 지원한다.
\(e\ ::=\ n\ |\ e + e\ |\ e – e\)
여기에 변수를 정의하는 식의 형태를 추가해야 한다.
\(x \in {\it Var}\)
\(e\ ::=\ \ldots\ |\ x = e; e\)
\(x\)는 변수 이름을 나타낸다. 일반적으로 변수의 이름은 어떤 문자열이다. \(“squareOf3”\)라는 문자열을 앞에서 변수 이름으로 사용했다. 그러나 모든 문자열이 변수의 이름으로 사용되지는 않는다. \(“42”\)와 같은 문자열은 수를 나타내는 데 사용하는 것이 보통이므로 변수 이름으로는 사용할 수 없다. 또, 언어에 \(“for”\), \(“while”\), \(“var”\)과 같이 언어에서 특별한 의미를 가지고 있는 키워드가 존재한다면 이런 문자열 역시도 변수 이름으로는 사용할 수 없다. 따라서 변수 이름의 집합이 모든 문자열의 집합이라고 말할 수 없다. 그렇다고 해서 복잡한 방법을 사용해 변수 이름의 집합에 어떤 문자열이 포함되는지 하나하나 정의하는 것은 귀찮은 일이다. 요약 문법을 정의하는 데 있어 그런 고민은 너무 ‘구체적인’ 고민이다. 그냥 어떤 집합이 있어 그 집합의 모든 원소가 변수의 이름이 될 수 있으며 그 집합은 정수 집합 등과 겹치지 않는다고 간주하면 그만이다. 그렇게만 생각해도 변수를 사용할 수 있는 언어의 요약 문법과 의미를 정의하는 데 아무 문제가 없다. 여기서는 \(\it Var\)이 변수 이름의 집합이다. 즉, \(x\)는 \(\it Var\)의 원소를 나타내며, \(\it Var\)이 실제로 무엇인지는 굳이 신경 쓰지 않겠다.
\(x = e_1; e_2\)라는 식이 있으면, \(x\)는 정의하는 변수의 이름이며, \(e_1\)은 그 변수의 값을 결정하는 식이고, \(e_2\)는 그 변수를 사용할 수 있는 식이다. 2장에서 말한 것처럼 요약 문법의 표기법에는 별 의미가 없다. \(x = e; e\) 대신 \({\sf val}\ x = e; e\)라든가 \({\sf let}\ x = e\ {\sf in}\ e\) 같은 표기를 사용해도 문제가 없다. 이 책에서는 적당히 파이썬과 C 등의 언어의 구체적 문법을 참고하여 요약 문법의 표기를 정했다. 요약 문법의 표기에 큰 의미를 둘 필요가 없다는 점에 대해서는 2장에서부터 여러 번 설명한 바 있으니, 앞으로는 더 이상 언급하지 않겠다.
\(x = e_1; e_2\) 형태의 식에서 \(x\)를 묶는 등장(binding occurrence)라고 부른다. \(x\)라는 이름의 변수를 정의하는 것을 \(x\)를 묶는다고 표현하기 때문이다. 구어체로는 영어 용어를 그대로 사용해서 \(x\)를 바인딩한다고 표현하는 경우가 많을 것이다. ‘묶는 등장’이라는 표현 자체는 흔히 사용되지 않지만 ‘묶기’나 ‘바인딩’은 이 책이 아니더라도 많이 사용하는 표현이니 기억해 두는 것이 좋다.
변수를 정의하는 식의 요약 문법을 살펴보았으니, 원래대로라면 이제 변수를 정의하는 식의 의미를 알아볼 차례이다. 그러나 아직 정의한 변수를 사용하는 방법은 다루지 않았다. 변수를 정의하는 식의 의미는 변수를 사용하는 식의 의미와 함께 다루는 편이 더 좋기 때문에, 변수 정의의 의미는 조금 있다 다루도록 하겠다.