프로그래밍 언어를 수학적 대상이 아니라 자연에서 발견되는 대상처럼 바라볼 때, 비형식 언어와 명세의 관계, 테스트의 역할, 그리고 도구/의미론 연구가 나아갈 방향이 어떻게 달라지는지를 논한다.
1990년대 초, 프로그래밍 언어 연구 공동체는 낙관적인 분위기에 젖어 있었다. 바로 얼마 전, 그 분야의 대표적 언어 둘—Scheme과 ML—이 자신들의 의미론을 형식화했기 때문이다. ML의 경우 Milner 외 공저자들이 쓴 한 권의 책 형태로 정리되었다. Scheme은 표준 문서 안에 지시적(denotational) 의미론을 제공했다. 그러니 이제는, 모든 언어가 결국 이 방향으로 가는 일만 남았다고 여겨졌다.
무엇이 잘못되었을까?
이론적 배경이 없는 실무 프로그래머라면, 형식 의미론이 대체 왜 중요한지 의아할 수 있다. 사실 그 이유는 당신도 쉽게 이해할 수 있는 것들이다. 다른 사람이 작성한 코드에 맞춰 프로그램을 작성할 때, 당신은 그 코드에 맞춰 프로그래밍할 수 있는 인터페이스, 즉 API가 있기를 바란다. API는 원격의 코드와 나누는 “대화”를 매개하고, 뭔가가 기대대로 동작하지 않을 때 누구를 탓해야 하는지에 대한 대화를 붙잡아 두는 닻 역할을 한다.
언어의 형식 의미론도 정확히 같은 일을 한다. 언어 자체가 당신이 상대하는 “원격 코드”다. 언어가 무엇을 하는지 가능한 한 정확하게—하지만 또한 가능한 한 이해하기 쉽게!—못 박아 두어야, 당신이 그 위에서 작업할 수 있다.
의미론은 언어 사용자만을 위한 인터페이스가 아니다. 도구 제작자에게도 핵심적인 인터페이스다. 그렇지 않으면 그들(도) 언어의 동작을 추측해야 한다. 비교적 단순해 보이는 변수 이름 변경 리팩터링조차 어떻게 잘못될 수 있는지에 대한 예를 보고 싶다면, 우리의 Python 의미론 부록 2를 보라. 의미론이 그런 종류의 오류가 절대 발생하지 않음을 보장해 주지는 않지만, 의미론이 없으면 그런 오류가 훨씬 더 발생하기 쉽다.
많은 연구자에게 프로그래밍 언어는 수학적 대상이라는 관점이 자명하다. 이는 자연스럽고 매력적인 관점이다. 언어는 결국 형식적 대상이며, 따라서 논리학에서 위상수학에 이르기까지 온갖 도구로 체계화하기에 적합하다. (형식) 명세는 이런 세계관의 자연스러운 귀결이다.
하지만 현실 세계에는 이 모델에 맞지 않는 프로그래밍 언어가 가득하다. 그중 일부는 심지어 프로그래밍 언어 의미론 연구자들이 만들었을 수도 있는데, 이는 곰곰이 생각해 볼 만한 일이다. 표면상 “세련되지 않은” 이들에 의해 만들어진 언어는 구현체 하나만 덜렁 내놓으며 무례하게 등장한다. 사람들은 그것을 집어 들고 유용함을 발견해 의미 있는 일을 하고, 백만 줄의 코드가 쌓일 즈음이면 다음 거대 “스크립팅 언어”가 탄생한다. 이런 것들을 비형식 언어(informal language)라고 부르자.
비형식 언어도 물론 형식 명세의 대상이 될 수 있다. 그러나 이 두 부류의 언어를 가르는 결정적 차이가 있다. 명세가 말하는 것과 구현이 하는 일이 다르다고 관찰되었다고 하자. 예를 들어 Standard ML의 경우, 구현자를 찾아가 명세에 담긴 논리를 함께 따라가며 구현이 왜 틀렸는지 설명할 수 있다. 구현자가 당신의 논리에 동의한다면, 그로 인해 얼마나 불행해지든 간에, 결국 자신의 구현이 잘못되었음을 인정하고 고쳐야 한다.
비형식 언어의 구현자는 그런 제약을 받지 않는다. 겉보기 명세와 자신의 구현 사이의 불일치에 직면하면, 그들은 마음껏 웃고, 명세의 혈통(?)을 의심하며, 무대 오른쪽으로 퇴장해 버릴 자유가 있다. 명세는 기껏해야 “언어가 무엇을 하고 있는지”를 따라갈 뿐, 실제로 그 행동을 지시하지는 못한다.
그렇다고 해서 명세가 쓸모없어지는 것은 아니다. 명세는 다음과 같은 역할을 할 수 있다.
크고 불투명한 구현 내부에서 무슨 일이 벌어지는지에 빛을 비춘다.
단순화될 필요가 있는 복잡한 설계를 지적한다.
프로그래머 오류, 보안 위반 등을 유발하기 쉬운 설계 측면을 강조한다.
도구 구축의 기반이 되는 등, 명세가 수행하는 다른 모든 용도를 제공한다.
하지만 이런 일들을 유용하게 해내려면, 명세는 비형식 언어의 현실과 자신이 부합함을 보여 주기 위해 상당한 노력을 기울여야 한다. 예컨대 구현체들과 동일한 테스트 스위트를 실행하는 식으로 말이다.
따라서 비형식 언어는 자연에서 발견되는 객체와 훨씬 더 닮아 있다.

우리는 그것을 밀어 보고, 찔러 보고, 쿡쿡 쑤셔 보면서 비밀을 내놓기를 바랄 수는 있다. 하지만—구현자의 도움 없이는—우리가 그 전체를 올바르게 특성화했는지 결코 확신할 수 없다.
그렇다고 체계적으로 접근할 수 없다는 뜻은 아니다. 지질학자들은 바위 앞에서 무력하게 허우적대지 않는다. 고등학교 화학 시간에 나는 미지의 물질에 SCODS 테스트로 접근하라고 배웠다. 상태(state), 색(color), 냄새(odor), 밀도(density), 용해도(solubility)를 확인하는 것이다. 그들은 “맛(taste)”에 해당하는 글자는 포함하지 않았는데, 아마도 고등학교 화학 실험실의 현실보다는 그들의 이상을 반영한 것이었을지 모른다. 즉, 우리는 분류의 몇 가지 차원을 정해 두고, 그 주요 축을 따라 나아간다.
프로그래밍 언어에도 이런 축이 있다. 스코프 규칙, 평가 의미론, 타입 시스템 등으로 언어를 이야기할 수 있다. 실제로 프로그래머 경력의 상당 부분은 비형식 언어를 마주하고 그것을 빠르게 이해해야 하는 데 쓰일 것이기 때문에, 나는 오래전부터 프로그래밍 언어를 가르치는 방식도 이런 차원들에 따라 나뉘어야 한다고 느껴 왔다.
비형식 언어의 문제는 사라지지 않을 것이다. 헌신적인 아마추어가 새로운 언어의 구현체를 만들어 낼 수 있는 한 말이다. 실제로 비형식 언어의 공간은 수학적 언어의 공간보다 훨씬 더 크고, 또한 계속 커지고 있다. 2015년에 나는 이런 슬라이드를 만들었다.

이 슬라이드는 당시의 전형적인 클라이언트 사이드 웹 프로그램이 올라서 있던 스택을 보여 준다. 하지만 이것이 실제로 보여 주는 것은 훨씬 더 미묘하면서도 중요한 사실이다.
프로그래머는 “언어”로만 프로그래밍하지 않는다.
대신, 어떤 복잡한 조합—언어들, 라이브러리들, 프레임워크들(이들은 자신만의 동작적 행위를 강제할 수 있다. 예: 값 전달(call-by-value) 언어를 마치 반응형(reactive) 언어처럼 보이게 만들기도 한다)—로 프로그래밍한다. 프로그래머에게 필요한 의미론은 바닥 근처에 놓인 언어 하나에 대한 것이 아니라, 자신의 프로그램이 올라타 있는 그 산 전체에 대한 것이다.
그런 의미론이 어떤 모습일 수 있는지에 대한 예시로는, DOM의 조작적 의미론에 관한 우리의 연구(이건 거대한 제어 연산자(control operator)다!), 그리고 jQuery의 타입 구조에 관한 연구를 보라. 하지만 이것들은 아주 작은 조각의 아주 작은 조각일 뿐이다. 어떻게 하면 그 산을 실제로 더 올라가며, 산이 계속 땅에서 솟아오르듯 확장될 때도 그 위에서 위치를 유지할 수 있을까?
데이트 피커 라이브러리의 작성자가 의미론 연구자이기까지 하리라 기대할 수는 없다. 하지만 그렇다고 해서 결연히 “우린 절대 거기에 도달할 수 없다”고 결론 내려서도 안 된다. 오히려 우리는 유용한 분업이 존재함을 인정해야 한다. 비형식 언어 설계자들의 관행을 꾸짖는 대신, 의미론 연구자들은 그 간극을 메울 방법을 찾아야 한다. 다음을 염두에 두면서:
비형식에서 형식으로는, 형식적인 수단만으로는 나아갈 수 없다.
우리가 제안하는 것은 다음 조건을 만족해야 한다.
구현자가 만들기(기술 수준 측면에서) 합리적일 것.
구현자가 만들었을 때 그들에게도 유용할 것.
우리가 소비하고 작업하기에도 유용할 것.
특히 좋은 중간 매개체(proxy) 하나는 어떤 형태의 테스트 스위트다. 테스트는 마땅히 받아야 할 존중을 항상 받지는 못하지만, 그것 역시 명세의 한 형태다. 나는 테스트를 “아래로부터의 명세(specifications from below)”라고 부르는 것을 좋아한다. 개별 사례에서 무엇이 일어나야 하는지를 정확히 정의하기 때문이다. 반면 전형적인 형식 명세는 “위로부터의 명세(specification from above)”로, 사례들의 한 가족 전체에 걸쳐 무엇이 일어나는지를 정의한다. 물론 궁극적으로는 “위로부터의 명세”가 필요하지만, “아래로부터의 명세”가 개발자에게도 유용하고 그들이 만들어 내는 데에도 숙련되어 있다는 점은 주목할 가치가 있다. 최근 언어들에서 “적합성 테스트 스위트(conformance suites)”나 “리트머스 테스트(litmus tests)”를 제공하는 경향은 특히 유망하다. (예컨대 ECMAScript 5.1의 strict mode 형식화에 관한 우리의 연구는 ECMAScript 적합성 스위트를 활용했다.)
이를 어떻게 활용할지는 여전히 다소 열린 질문이다. 가장 뻔한 용도는 우리의 의미론을 테스트하는 것이다. Arjun Guha와 나는—어떤 이들에게는 공포일지도 모르지만—이를 tested semantics(테스트된 의미론)라고 불렀다. 하지만 우리는 이 테스트들을 “아래”에서 “위”로 일반화할 수도 있을 것이다. 즉, 이는 합성(synthesis) 문제다. 이를 위해서는 또한 의미 있는 의미론을 산출할 수 있는 어떤 구조가 필요하다. The Next 700 Semantics에 관한 우리의 논문은, 이 연구 방향에서 고려해야 할 윤곽 몇 가지를 제시한다.