docs site를 Mere로 쓰기: 애플리케이션 규모의 dogfood
언어는 모든 테스트를 통과해도, 그것으로 무언가 진짜를 만들기가 비참할 수 있다. 그것을 아는 길은, 진짜를 만드는 것이다 —— 그래서 Mere 자신의 문서 사이트를, Mere로 쓰인 도구로 생성하기로 했다. 요점은 사이트가 아니다; 시도가 드러낸 것, 어느 primitive가 실제로 부족한지의 우선순위 붙은 목록과, 진짜 문서를 진짜 코드에 통과시켜야만 부딪히는 마찰이다.
Part IV의 끝까지, Mere는 네 방식으로 돌고, 그것들을 동일한 출력에 유지하며, 그 양쪽의 코드와 말할 수 있다. 그 하나하나가 테스트로 검증된다. 하지만, 어떤 테스트 스위트도 답하지 않는 물음이 있다: 언어는 실제로 무언가를 만들기에 유쾌한가? 언어는 옳으면서도, 여전히 비참할 수 있다 —— 손을 뻗어야 비로소 알아채는 구멍투성이 표준 라이브러리, 세 번째로 쓰면 어색하다고 판명되는 관용구. 그것을 아는 유일한 길은, 진짜를 만드는 것이고, 프로젝트가 고른 진짜는 정곡을 찔렀다: Mere 자신의 문서 사이트를, Mere로 쓰인 도구로 생성한다.
왜 docs site인가, 그리고 왜 그 형태인가
선택은 일부러 자기 참조적이다. Mere의 문서 —— 튜토리얼, 언어 레퍼런스, 표준 라이브러리 레퍼런스, 관용구 페이지, 그 외, 아홉 파일에 걸친 수천 줄의 Markdown —— 은, 열람 가능한 웹사이트가 되어야 한다. 그 변환을 행하는 정적 사이트 생성기를 Mere로 쓰는 것은, 이 시도에 둘을 동시에 증명하게 한다: docs가 세워지고, 언어가 진짜 도구를 짊어질 수 있음을 보인다. “Mere로 쓰인 도구가 Mere의 문서를 생성한다”는, 주장이 아니라 가리킬 수 있는 실용성의 증거다.
그리고 부품의 대부분은 이미, 도중에 세워진 contrib 라이브러리로 존재했다: Markdown→HTML 변환기,
{{KEY}} 템플릿 엔진, 커맨드라인 인자 파서, JSON 리더. 사이트 생성기는 대부분, 그것들을 배선하는
문제다 —— Markdown 파일을 읽고, 저마다를 변환하고, 내비게이션 바가 붙은 템플릿으로 감싸고, index와
스타일시트를 써 낸다. 종이 위에서는, 발명이 아니라 조립으로 보인다.
진짜 성과는 gap 분석이다
dogfood를 가치 있게 하는 것은, 성과물이 아니다; 시도가 드러내는 것이다. 실제로 생성기를 세우려 앉으면, “표준 라이브러리에는 구멍이 있다”는 막연한 감각이, 어느 것이 실제로 무는가의, 구체적이고 우선순위 붙은 목록으로 바뀌었다.
몇몇 구멍은 진짜 OS 접근을 요하여, 그래서 새 builtin이 되어야 했다: 디렉터리의 파일을 열거하는 것,
디렉터리 트리를 만드는 것은, 언어가 아직 할 수 없는 POSIX 호출이다. 다른 것은, 컴파일러를 전혀 요하지
않는다고 판명되었다 —— 경로를 잇는 것, basename·dirname·확장자를 취하는 것은, 모두 수십 줄의 평범한
Mere이므로, builtin이 아니라 작은 contrib/path 라이브러리가 되었다. 프론트매터 파싱 —— 각 문서
첫머리의 --- title: ... --- 블록 —— 은, YAML의 최소 한 조각, 중첩 없는 키와 값만을 요하여, 이것도
짧은 라이브러리가 되었다. 그 분류(triage)야말로, 이 시도의 실제 성과다: 어깨를 으쓱할 뿐인 “stdlib가
불완전하다”가 아니라, “이 둘은 OS primitive를 요하고, 이 넷은 순수한 라이브러리 코드, 이 순서로” ——
추상적으로 추측되는 것이 아니라, 진짜 필요에서 나온 작업 목록이다.
세우는 것이 강요한 결정은, 모두 같은 조성이었다: 최소의 정직한 판을 취하고, 나머지는 미룬다. 프론트 매터는 축소 YAML이고, 광대한 사양 전부가 아니다 —— 그리고 특히, 설정으로 평가되는 Mere 소스가 아니다. 그것은 너무 강력하고 공격면이 너무 크다고 하여 단적으로 기각되었다. 생성기는 단일한 커맨드라인 프로그램으로 시작하여, 나중에 누군가 부품을 따로 원할 때에만 재사용 가능한 라이브러리로 나눈다. 저마다, 언어가 설파하는 규율을, 진짜 태스크의 압력 아래에서, 언어 자신의 도구에 적용한 것이다.
진짜 문서만이 낳는 마찰
그리고, 설계로 회피할 수 없고, 발견할 수밖에 없는 버그가 있었다. 템플릿 엔진은 치환점을 {{KEY}}로
짚는다. Mere의 문서는, 프로그래밍 언어에 대한 문서이기에, 예 안에 {{KEY}} 나열을 리터럴 텍스트로
포함한다 —— 그리고 템플릿 엔진은, 그것을 보고, 치환하려 했다. docs를 세우는 도구가, docs의 내용과
충돌했다.
이것은 바로, 지난 회가 천 줄의 프로그램을 컴파일하여 찾은 종류의 문제다: 어떤 작은 테스트에서도 보이지 않는다. 작은 테스트는, 진짜 내용이 포함하는 적대적 입력을 포함하지 않기 때문이다. 당신은, 자신의 docs가 자신의 템플릿 구문에 대해 이야기하리라 예기하지 않는다; docs가 망가져 나왔을 때 알아챈다. 수정은 평범하다 —— 이스케이프 규약이거나, 다른 구분자거나 —— 하지만 발견이야말로 dogfood 가치의 전부다. 진짜 내용은, 어떤 테스트 작성자도 쓰려고 생각하지 않는 적이다.
본편을 위한 준비 운동
docs site를 세우는 것은, 프로젝트의 방법론 —— 종이 위 trial, 그다음 dogfood —— 을, 애플리케이션 통째의 규모로 적용한 것이고, 그것은 Part IV를 옳은 조성으로 닫는다: 코드 생성은 테스트를 통과함으로써 증명되는 것이 아니라, 진짜 프로그램을 짊어짐으로써 증명된다. 사이트 생성기는, Mere가 참된 도구를 세울 수 있음을 보이고, 실제로 중요한 stdlib의 구멍을 들춰내고, 진짜 사용만이 낳는 마찰에 꼭 부딪혔다.
하지만 문서를 생성하는 것은, 작은 자기 참조다 —— Mere에 대한 텍스트를 처리하는 Mere 프로그램. 훨씬 큰 것이 기다린다, 이 연재 전체가 향해 온 것: Mere 자신을 처리하는 Mere 프로그램이다. 언어가 docs 생성기를 짊어질 수 있다면, 자기 자신의 컴파일러를 짊어질 수 있는가 —— 렉서, 파서, 타입 검사기, 평가기, 코드 생성기, 모두 Mere로 쓰이고, Mere를 컴파일하는? 그것이 셀프 호스팅이고, 다음 Part의 주제다. 다음, Part V가 열린다: 프론트엔드 —— 렉서, 파서, 공유된 구문 트리 —— 를 언어 자신으로 쓰기.