프로그래밍에서 제어의 개념과 역사, 이 책의 구성과 독자별 읽는 방법을 소개한다.
최초의 프로그래머블 장치인 스트리트 오르간과 자카드 직조기는 18세기 유럽에 등장했다. 둘 다 천공 카드를 통해 제어되었다. 카드의 구멍 유무가 어떤 음을 연주할지, 어떤 실을 선택할지를 결정한다. 카드는 줄로 나뉘며 각 줄은 순차적으로, 하나씩 실행된다. 그림0.1에서 보이듯, 종종 카드들이 고리(loop)를 이룬다. 마지막 카드의 끝이 첫 카드의 시작에 부착되어 음악이나 무늬가 무한히 반복되도록 하는 것이다.
그림 0.1: 자카드 직조기의 제어 장치. 천공 카드가 고리 형태로 배치되어 있다. (CC BY-NC-SA, Heinz Nixdorf MuseumsForum.)
나는 컴퓨터 프로그래밍에서의 시퀀스(sequence)와 루프(loop)라는 개념이 오르간과 직조기의 천공 카드를 다루던 관행에서 직접 유래했다고 생각하고 싶다. 어쨌든 이 두 개념은 현대 컴퓨터—메모리에 프로그램을 저장하는 프로그래머블 전자 계산기—가 1940년대 중반 등장한 이래 핵심적인 역할을 해 왔다. 오늘날에도 기계어 프로그램은 여전히 메모리에 저장된 프로세서 명령들로 이루어져 있으며, 분기(branch)나 점프(jump) 명령을 제외하면 하나씩 순차적으로 실행된다. 분기/점프 명령은 지정된 코드 주소에서 실행을 계속하게 한다. (예시는 그림0.2 참조.)
그림 0.2: 피보나치 수를 계산하여 출력 포트 32로 보내는 x86 기계어 코드. 왼쪽의 화살표가 제어 흐름을 보여준다. PC 15에서 PC 10으로의 점프가 무한 루프를 만든다.
초기의 프로그래밍 언어(어셈블리 언어, 오토코더, Fortran I 등)는 제어를 기계 수준의 관점으로 드러냈다. 기본 명령은 순차적으로 실행되고, 유명한 goto 명령처럼 무조건 또는 조건부 점프를 통해 계산을 반복하거나 건너뛸 수 있다. 1950년대 말까지 이런 접근의 한계가 분명해지면서, Algol 60과 그 수많은 후계 언어와 같은 이후의 프로그래밍 언어들은 더 높은 수준의 제어 구조를 도입했다.
한편으로 함수형 프로그래밍(Lisp, 1960)과 논리 프로그래밍(Prolog, 1972) 같은 다른 패러다임도 등장했다. 이들 언어는 제어를 대부분 암시적으로 남겨 둔다. 소스 코드는 어떻게 계산하는가보다는 어떤 값이나 술어를 계산해야 하는가를 정의하는 데 초점을 맞춘다. 그러나 제어는 여전히 다른 형태로 존재한다. 프로그램 실행에 사용되는 평가 및 해소(resolution) 전략 속에 있으며, 함수형 언어의 경우 제어 흐름을 컨티뉴에이션(continuation)이라 불리는 함수 값으로 표현하고, 컨티뉴에이션을 조작하는 라이브러리 함수로 사용자 정의 제어 구조를 프로그래밍할 수 있다.
이 책은 제어의 개념과 패러다임을 길잡이 삼아 프로그래밍 언어의 설계 공간과 역사적 시간을 가로지르는 여정이다. 우리는 다음과 같은 질문에 답하고자 한다.
우리의 접근은 주로 서술적이고, 종종 비교적이며, 때때로 형식적이지만 규범적(prescriptive)이지는 않다. 연대표는 최초의 어셈블리 언어가 개발된 1947년부터 효과 핸들러가 처음으로 주류 프로그래밍 언어에 통합된 2022년까지를 포괄한다. 또한 프로그래밍 예제에서부터 프로그래밍 언어 이론에 이르기까지 스펙트럼 전반을 아우른다.
이 책의 나머지 부분은 네 부분으로 나뉜다.
제 I부: 명령형 언어를 위한 제어 구조는 전통적인 명령형 및 객체지향 언어의 제어 구조를 설명하고, 비교하며, 그 역사적 발전을 보여준다.
제 II부: 함수형 언어를 위한 제어 연산자는 컨티뉴에이션이라 불리는 데이터로 제어 흐름을 조작하는 함수형 프로그래밍 언어에 초점을 맞추며, 이를 통해 라이브러리로 사용자 정의 제어 구조를 정의할 수 있게 한다.
제 III부: 예외와 모나드에서 대수적 효과와 핸들러로는 함수형 언어를 위한 제어 연산자의 최근 발전인 효과 핸들러와 그 기반 이론인 대수적 효과를 다룬다.
제 IV부: 제어와 효과에 대한 추론은 타입 시스템과 프로그램 논리를 이용해 제어와 효과의 안전성과 정당성을 확립하는 방법을 보여준다.
독자는 C, C++, Java, Python과 같은 하나의 명령형 언어와 Haskell, OCaml, Scheme, SML과 같은 하나의 함수형 언어에 익숙하다고 가정한다. 동반 웹사이트 XXX에는 이 책에 제시된 코드 예제가 모여 있으며, 이를 실행하는 방법을 안내한다. 보다 기술적인 장들은 프로그래밍 언어 이론에 대한 어느 정도의 지식을 전제하며, 예컨대 전자 교과서 Programming Languages Foundations(Pierce et al., 2025)에서 찾을 수 있다.
이 책을 읽는 방법은 여러 가지가 있다. 주로 프로그래밍 언어 간 비교와 그 역사적 발전에 관심이 있는 독자는 장1부터5까지와8부터10까지에 집중할 수 있다. 함수형 프로그래밍과 컨티뉴에이션의 활용에 관심이 있는 독자는 제 I부를 건너뛰고 장5부터10까지에 집중할 수 있다. 이 중 일부에 익숙하지만 대수적 효과의 최근 발전을 더 알고 싶은 독자는 제 III부에서 시작해 장9부터15까지에 집중할 수 있다. 마지막으로, 프로그래밍 언어의 기초와 이를 이용한 프로그램 검증에 관심이 있는 독자는 장6, 8, 그리고11부터 15까지를 흥미롭게 읽을 것이다.
이 책의 기술적 난이도는 전체적으로 선형적으로 증가하지 않는다. 오히려 각 부 안에서, 그리고 종종 각 장 안에서 증가한다. 어떤 대목이 지나치게 기술적으로 느껴진다면, 다음 장이나 심지어 다음 부로 건너뛰기를 권한다.