이미 차지된 이름

Scheme 인터프리터는 함수를 `run` 이라 이름 붙였고, C backend 에서는 그 호출이 대신 셸 명령으로 컴파일되었다 —— `run` 도 내장이고, 컴파일러가 사용자의 정의를 고려하기 전에 내장을 매치했기 때문이다. 인터프리터는 늘 이것을 옳게 했으므로 컴파일 backend 만 틀렸다. 같은 버그의 느린 행진의 다섯 번째 이름이었다 —— index, remove, acct, dup, 그리고 이번엔 run —— 그리고 패턴은 사례를 하나씩 다루기를 멈추고 종류째 끝내기에 충분히 분명했다. 하나의 헬퍼가 이제 어느 내장이 이름을 주장하기 전에 그 이름이 사용자에게 바인딩되었는지를 묻고, 충돌하기 쉬운 약 30 개의 내장을 그 호출 지점에서 지킨다. 가드는 증명 가능하게 안전하다 —— 사용자가 실제로 내장을 shadow 했을 때만 동작을 바꾸므로, 그러지 않은 모든 프로그램은 바이트 단위로 불변이다. 사용자의 정의는 내장을 이겨야 하고, 이제 그렇다.

merecompilername-resolutioncodegenlanguage-design

Scheme 인터프리터에는 run 이라는 이름의 함수가 있었다 —— 각 최상위 형을 읽고 평가하는 루프다. 인터프리터에서는 동작했다. C backend 에서는 프로그램이 기이한 짓을 했다. 자신의 소스 텍스트를 셸 명령으로 실행하려 한 것이다. 원인은 run 도 내장이고 —— 외부 명령줄을 실행하는 수단 —— 컴파일러의 코드 생성기가 사용자가 같은 이름의 함수를 정의했음을 고려하기 전에 호출 지점에서 그 내장을 매치한 것이었다. 인터프리터는 이름을 보통의 방식으로 해석해 나중의 바인딩이 앞의 것을 shadow 하므로 늘 올발랐다. 컴파일 backend 만 먼저 내장으로 손을 뻗었다.

다섯 번째는 패턴

이것은 충돌한 첫 이름이 아니었다. 다섯 번째였다. 이전 probe 는 indexremove 가 C 라이브러리 심볼과 충돌해 넘어졌고, 장부는 함수를 acct 라 이름 붙였는데 그것은 POSIX 호출이다. 각각 그 자리에서 수정되었고 —— 이건 새니타이즈, 저건 개명 —— 각 수정은 위험한 이름의 목록이 본질적으로 불완전하다고 조용히 인정했다. 같은 형태의 다섯 사례는 사례를 고치는 것이 옳은 수가 아니게 되는 지점이다. 버그는 사실 run 이나 indexacct 에 관한 것이 아니었다. 사용자가 이미 바인딩한 이름을 내장이 주장하게 하는 해석 순서에 관한 것이었다. 수정은 종류를 끝내야 했지, 여섯 번째 이름을 목록에 더하는 것이 아니었다.

사용자를 이기게 하다

코드베이스에는 이미 전례가 묻혀 있었다. 흔한 이름을 가진 몇몇 내장 —— 스레드 join, 문자 술어 —— 은 이름이 사용자에게 바인딩되어 있으면 비켜서는 가드를 가졌고, 각각이 누군가를 물 때마다 하나씩 더해졌다. 수정은 그 전례를 하나의 헬퍼로 일반화한다. 하나의 물음을 묻는다 —— 이 이름은 local 로 바인딩되었는가, 둘러싼 스코프에서 포획되었는가, 안쪽 함수에서 들어 올려졌는가, 최상위에서 정의되었는가 —— 그리고 그 가드를 사용자가 그럴듯하게 고를 수 있는 약 30 개의 내장에 놓는다. run, spawn, even, odd, show, abs, 그 밖에. 답이 yes 면 호출 지점은 보통의 사용자 호출 길로 떨어지고, no 면 내장이 이전과 똑같이 나온다.

증명 가능하게 안전, 그리고 문제의 절반이 닫히다

변경을 넓게, 한꺼번에 넣을 수 있었던 것은 그것이 구성상 안전하기 때문이다. 가드는 사용자가 실제로 이름을 바인딩했을 때만 발화한다 —— 그리고 run 을 함수로 바인딩하는 프로그램은 컴파일러가 이미 잘못 컴파일하던 프로그램이다. 내장을 shadow 하지 않는 모든 프로그램은 이전과 바이트 단위로 동일한 출력을 낸다. 테스트 스위트 전체가 그것을 확인했고, 변함없이 통과했다. 이것이 닫는 것은 예약 이름 문제의 내장 절반이다 —— 사용자의 정의가 호출 지점에서 Mere 자신의 내장을 이긴다. 나머지 절반, 생성 코드에서 C 키워드나 POSIX 심볼과의 충돌은 여전히 이름 새니타이저가 다루고, 완전히 일반적인 수정 —— 모든 사용자 이름을 이름공간화해 아무것도 충돌할 수 없게 하는 것 —— 은 미뤄진 채다. 내장 절반이 닫힌 지금 그것은 더 작은 남은 이득을 위한 더 큰 변경이기 때문이다. 다섯 번 재발한 버그의 종류가 이제 구조적으로 사라졌다. 그것은 그 어느 한 사례에 대한 수정보다 가치가 있다.

← Back to Mere: 언어를 만들다