간결성은 힘이다

2002년 5월

| "대수 기호에 의해 작은 공간에 압축된 의미의 양은 우리가 익숙하게 수행하는 추론을 용이하게 하는 또 다른 상황이다."

  • 찰스 배비지, 아이버슨의 튜링상 강연에서 인용

LL1 메일링 리스트에서 너드들의 복수가 제기한 문제들에 대한 논의 중, 폴 프레스코드가 내 마음에 깊이 남는 말을 썼다.

파이썬의 목표는 규칙성과 가독성이지, 간결성이 아니다.

표면적으로는, 이는 프로그래밍 언어에 대해 주장하기에는 다소 치명적인 것처럼 보인다. 내가 아는 한, 간결성 = 힘이다. 그렇다면 대입하면 다음과 같다.

파이썬의 목표는 규칙성과 가독성이지, 힘이 아니다.

그리고 이것은 당신이 원할 만한 상충 관계(만약 그렇다면)처럼 보이지 않는다. 이는 파이썬의 목표가 프로그래밍 언어로서 효과적이지 않다는 말과 크게 다르지 않다.

간결성 = 힘인가? 이것은 나에게 중요한 질문으로 보인다. 아마도 언어 설계에 관심 있는 사람에게 가장 중요한 질문일 것이며, 직접적으로 다루는 것이 유용할 질문이다. 아직 답이 단순히 '예'라고 확신하지는 않지만, 시작하기에 좋은 가설인 것 같다.

가설

내 가설은 간결성이 힘이거나, 특수한 경우를 제외하고는 동일하게 취급할 수 있을 만큼 충분히 가깝다는 것이다.

내 생각에 간결성이야말로 프로그래밍 언어가 _존재하는 이유_이다. 컴퓨터는 기계어로 직접 무엇을 해야 할지 지시받는 것에 똑같이 만족할 것이다. 우리가 고수준 언어를 개발하는 수고를 하는 주된 이유는 지렛대 효과를 얻기 위함이라고 생각한다. 그래서 1000줄의 기계어가 필요한 것을 고수준 언어 10줄로 말하고(더 중요하게는 생각할 수) 있도록 하기 위함이다. 다시 말해, 고수준 언어의 핵심은 소스 코드를 더 작게 만드는 것이다.

만약 더 작은 소스 코드가 고수준 언어의 목적이라면, 그리고 어떤 것의 힘이 그것이 목적을 얼마나 잘 달성하는지에 달려 있다면, 프로그래밍 언어의 힘을 측정하는 척도는 프로그램의 크기를 얼마나 작게 만드는가이다.

반대로, 프로그램을 작게 만들지 못하는 언어는 잘 들지 않는 칼이나 읽기 어려운 인쇄물처럼, 프로그래밍 언어가 해야 할 일을 제대로 못하는 것이다.

측정 기준

그런데 어떤 의미에서 '작은' 것인가? 코드 크기의 가장 일반적인 측정 기준은 코드 라인 수이다. 하지만 나는 이 측정 기준이 가장 흔한 이유는 측정하기 가장 쉽기 때문이라고 생각한다. 아무도 그것이 프로그램 길이의 진정한 척도라고 진정으로 믿는다고 생각하지 않는다. 다른 언어들은 한 줄에 얼마나 많은 것을 넣어야 하는지에 대해 다른 관례를 가지고 있다. C언어에서는 많은 줄에 구분자 한두 개 외에는 아무것도 없는 경우가 많다.

또 다른 쉬운 테스트는 프로그램의 문자 수이지만, 이것도 그리 좋지 않다. 어떤 언어(예를 들어 펄)는 다른 언어보다 짧은 식별자를 사용하기 때문이다.

나는 프로그램 크기의 더 나은 측정 기준은 요소의 수라고 생각한다. 여기서 요소란 소스 코드를 나타내는 트리를 그렸을 때 개별적인 노드가 될 수 있는 모든 것을 말한다. 변수나 함수의 이름은 요소이다. 정수나 부동 소수점 숫자는 요소이다. 리터럴 텍스트의 한 부분은 요소이다. 패턴의 요소나 형식 지시어는 요소이다. 새로운 블록은 요소이다. 경계선상의 경우도 있지만(-5는 두 요소인가 한 요소인가?) 대부분의 경우 모든 언어에서 동일하므로 비교에 큰 영향을 미치지 않는다고 생각한다.

이 측정 기준은 구체화될 필요가 있고, 특정 언어의 경우 해석이 필요할 수 있지만, 나는 그것이 프로그램이 가진 부분의 수를 측정하려는 올바른 시도라고 생각한다. 이 연습에서 그릴 트리는 프로그램을 구상하기 위해 머릿속으로 만들어야 하는 것이며, 따라서 그 크기는 프로그램을 작성하거나 읽는 데 필요한 작업량에 비례한다고 생각한다.

설계

이러한 종류의 측정 기준은 다른 언어들을 비교할 수 있게 해주지만, 적어도 나에게는 그것이 주된 가치는 아니다. 간결성 테스트의 주된 가치는 언어를 _설계_하는 데 있어 지침으로서의 가치이다. 가장 유용한 언어 간 비교는 동일한 언어의 두 가지 잠재적 변형 사이에서 이루어진다. 언어에서 프로그램을 더 짧게 만들기 위해 무엇을 할 수 있을까?

만약 프로그램의 개념적 부담이 그 복잡성에 비례하고, 주어진 프로그래머가 고정된 개념적 부담을 감당할 수 있다면, 이는 프로그래머가 가장 많은 일을 할 수 있도록 무엇을 할 수 있을까 묻는 것과 같다. 그리고 그것은 나에게 좋은 언어를 어떻게 설계할 수 있을까 묻는 것과 동일하게 보인다.

(덧붙여 말하자면, 언어를 설계하는 것만큼 "모든 언어는 동등하다"는 오래된 통념이 거짓임을 명백히 보여주는 것은 없다. 새로운 언어를 설계할 때, 당신은 끊임없이 두 가지 언어—즉 x를 했을 때의 언어와 하지 않았을 때의 언어—를 비교하여 어느 것이 더 나은지 결정한다. 만약 이것이 정말 무의미한 질문이라면, 동전을 던지는 것이나 마찬가지일 것이다.)

간결성을 목표로 하는 것은 새로운 아이디어를 찾는 좋은 방법인 것 같다. 만약 당신이 여러 다른 프로그램을 더 짧게 만들 수 있는 무언가를 할 수 있다면, 그것은 아마 우연이 아닐 것이다: 당신은 아마 유용한 새로운 추상화를 발견했을 것이다. 반복되는 패턴을 찾기 위해 소스 코드를 검색하여 도움을 주는 프로그램을 작성할 수도 있을 것이다. 다른 언어들 중에서도 간결성으로 명성이 높은 언어들이 새로운 아이디어를 얻을 수 있는 대상이 될 것이다: 포스, 조이, 아이콘.

비교

내가 아는 한, 이 문제들에 대해 처음으로 글을 쓴 사람은 프레드 브룩스(Fred Brooks)의 『맨먼스 미신』에서였다. 그는 프로그래머들이 언어에 관계없이 하루에 거의 같은 양의 코드를 생성하는 것 같다고 썼다. 내가 20대 초반에 이것을 처음 읽었을 때, 그것은 나에게 큰 놀라움이었고 엄청난 함의를 가지고 있는 것처럼 보였다. 그것은 (a) 소프트웨어를 더 빨리 작성하는 유일한 방법은 더 간결한 언어를 사용하는 것이며, (b) 이를 위해 수고를 아끼지 않는 사람은 그렇지 않은 경쟁자들을 뒤처지게 할 수 있다는 것을 의미했다.

브룩스의 가설은, 만약 사실이라면, 해킹의 핵심에 있는 것처럼 보인다. 그 이후로 나는 공식적인 연구부터 개별 프로젝트에 대한 일화에 이르기까지 이 질문에 대해 얻을 수 있는 모든 증거에 세심한 주의를 기울여왔다. 그의 주장을 반박할 만한 것은 아무것도 보지 못했다.

아직 나에게 결정적이라고 보이는 증거는 보지 못했고, 앞으로도 기대하지 않는다. 루츠 프레첼트(Lutz Prechelt)의 프로그래밍 언어 비교와 같은 연구들은 내가 예상했던 종류의 결과를 도출하지만, 의미 있는 테스트가 되기에는 너무 짧은 문제들을 사용하는 경향이 있다. 언어에 대한 더 나은 테스트는 한 달이 걸리는 프로그램을 작성할 때 어떤 일이 일어나는가이다. 그리고 유일한 진정한 테스트는, 만약 당신이 나처럼 언어의 주된 목적이 (생각해낸 것을 컴퓨터에게 지시하는 것만이 아니라) 사고하기에 좋다는 것이라고 믿는다면, 그 언어로 어떤 새로운 것을 작성할 수 있는가이다. 따라서 미리 정의된 사양을 충족해야 하는 모든 언어 비교는 약간 잘못된 것을 테스트하는 것이다.

언어의 진정한 테스트는 새로운 문제를 얼마나 잘 발견하고 해결할 수 있는지이지, 다른 사람이 이미 공식화한 문제를 해결하는 데 얼마나 잘 사용할 수 있는지 아니다. 이 두 가지는 상당히 다른 기준이다. 예술에서 자수나 모자이크와 같은 매체는 만들고자 하는 것을 미리 알고 있다면 잘 작동하지만, 그렇지 않다면 형편없다. 이미지를 만들면서 발견하고 싶을 때—예를 들어 사람의 이미지처럼 복잡한 것을 만들 때처럼—연필이나 수묵화, 유화와 같은 더 유동적인 매체를 사용해야 한다. 실제로 태피스트리와 모자이크는 먼저 그림을 그린 다음 그것을 복사하는 방식으로 만들어진다. (원래 "카툰"이라는 단어는 이러한 목적으로 그려진 그림을 묘사하는 데 사용되었다).

이것이 의미하는 바는 우리가 프로그래밍 언어의 상대적 힘에 대한 정확한 비교를 가질 가능성이 거의 없다는 것이다. 우리는 정밀한 비교는 할 수 있겠지만, 정확한 비교는 할 수 없을 것이다. 특히 언어 비교를 목적으로 하는 명시적인 연구들은 아마도 작은 문제들을 사용하고, 필연적으로 미리 정의된 문제들을 사용할 것이기 때문에, 더 강력한 언어의 힘을 과소평가하는 경향이 있을 것이다.

현장 보고서는 "과학적" 연구보다 필연적으로 덜 정밀하겠지만, 더 의미 있을 가능성이 높다. 예를 들어, 에릭슨(Ericsson)의 울프 비거(Ulf Wiger)는 얼랭(Erlang)이 C++보다 4-10배 더 간결하며, 소프트웨어 개발 속도도 비례적으로 빠르다는 결론을 내린 연구를 수행했다:

에릭슨 내부 개발 프로젝트 간의 비교는 어떤 언어(얼랭, P렉스, C, C++, 자바)가 사용되었는지와는 다소 무관하게, 소프트웨어 개발의 모든 단계를 포함하여 시간당 유사한 라인 생산성을 나타낸다. 그렇다면 다른 언어들을 차별화하는 것은 소스 코드의 양이 된다.

이 연구는 또한 브룩스의 책에서 암시적으로만 다루어졌던 점(그는 디버그된 코드 라인을 측정했으므로)을 명시적으로 다룬다: 더 강력한 언어로 작성된 프로그램은 버그가 더 적은 경향이 있다. 이는 네트워크 스위치와 같은 애플리케이션에서는 프로그래머 생산성보다 더 중요할 수 있는 그 자체로 목적이 된다.

취향 테스트

궁극적으로, 나는 당신이 직감을 따라야 한다고 생각한다. 그 언어로 프로그래밍하는 것은 어떤 느낌인가? 최고의 언어를 찾거나(또는 설계하는) 방법은 언어가 사고를 얼마나 잘 돕는지에 대해 과민해진 다음, 가장 좋다고 느껴지는 언어를 선택/설계하는 것이라고 생각한다. 만약 어떤 언어 기능이 어색하거나 제한적이라면, 걱정하지 마라, 당신은 그것을 알게 될 것이다.

그러한 과민성은 대가를 치르게 될 것이다. 당신은 서투른 언어로 프로그래밍하는 것을 견딜 수 없게 될 것이다. 나는 매크로가 없는 언어로 프로그래밍하는 것이 참을 수 없을 정도로 제한적이라고 생각한다. 마치 동적 타이핑에 익숙한 사람이 모든 변수의 타입을 선언해야 하고 다른 타입의 객체 리스트를 만들 수 없는 언어로 다시 프로그래밍해야 하는 것이 참을 수 없을 정도로 제한적이라고 느끼는 것과 같다.

나만 그런 것이 아니다. 나는 이런 경험을 한 많은 리스프 해커들을 알고 있다. 사실, 프로그래밍 언어의 상대적 힘을 측정하는 가장 정확한 척도는 해당 언어를 아는 사람들 중 애플리케이션 도메인에 관계없이 그 언어를 사용할 수 있는 어떤 일이라도 기꺼이 맡으려는 사람들의 비율일 수 있다.

제한성

대부분의 해커들은 언어가 제한적이라고 느껴지는 것이 무엇을 의미하는지 안다고 생각한다. 그렇게 느낄 때 무슨 일이 일어나는가? 나는 그것이 가고 싶은 길이 막혀서 목적지에 가기 위해 먼 우회로를 택해야 할 때 느끼는 것과 같은 감정이라고 생각한다. 당신이 말하고 싶은 것이 있는데, 언어가 그것을 허락하지 않는 것이다.

여기서 실제로 일어나는 일은, 내 생각에, 제한적인 언어는 충분히 간결하지 않은 언어라는 것이다. 문제는 단순히 당신이 계획한 것을 말할 수 없다는 것이 아니다. 언어가 당신에게 강요하는 우회로가 _더 길다_는 것이다. 이 사고 실험을 해보라. 당신이 작성하고 싶은 어떤 프로그램이 있는데, 언어가 당신이 계획한 방식으로 표현하는 것을 허락하지 않고, 대신 더 짧은 다른 방식으로 프로그램을 작성하도록 강요한다고 가정해보자. 적어도 나에게는 그것이 그리 제한적으로 느껴지지 않을 것이다. 그것은 당신이 가고 싶은 길이 막혔는데, 교차로의 경찰관이 우회로 대신 지름길로 안내해주는 것과 같을 것이다. 훌륭하다!

나는 제한적이라는 느낌의 대부분(90%?)이 당신이 머릿속에 가지고 있는 프로그램보다 언어로 작성하는 프로그램을 더 길게 만들어야 한다는 강요에서 온다고 생각한다. 제한성은 대부분 간결성의 부족이다. 따라서 언어가 제한적이라고 느껴질 때, 그것이 (대부분) 의미하는 바는 충분히 간결하지 않다는 것이고, 언어가 간결하지 않으면 제한적으로 느껴질 것이다.

가독성

내가 시작할 때 인용한 구절은 두 가지 다른 특성, 즉 규칙성과 가독성을 언급한다. 나는 규칙성이 무엇인지, 또는 규칙적이고 가독성 있는 코드가 단순히 가독성 있는 코드에 비해 어떤 이점이 있는지(있다면) 확실하지 않다. 하지만 나는 가독성이 무엇을 의미하는지 안다고 생각하며, 그것이 간결성과도 관련이 있다고 생각한다.

여기서 우리는 개별 코드 라인의 가독성과 전체 프로그램의 가독성을 구별하는 데 신중해야 한다. 중요한 것은 후자이다. 나는 베이직(Basic) 한 줄이 리스프(Lisp) 한 줄보다 더 읽기 쉬울 가능성이 높다는 데 동의한다. 하지만 베이직으로 작성된 프로그램은 리스프로 작성된 동일한 프로그램보다 더 많은 줄을 가질 것이다 (특히 그린스펀랜드(Greenspunland)로 넘어가면). 베이직 프로그램을 읽는 데 드는 총 노력은 분명히 더 클 것이다.

총 노력 = 줄당 노력 x 줄 수

나는 가독성이 힘만큼 간결성에 직접적으로 비례한다고 확신하지는 않지만, 확실히 간결성은 가독성의 한 요소(수학적 의미에서; 위 방정식을 보라)이다. 따라서 언어의 목표가 간결성이 아니라 가독성이라고 말하는 것은 의미가 없을 수도 있다; 그것은 목표가 가독성이지 가독성이 아니라고 말하는 것과 같을 수 있다.

줄당 가독성이, 언어를 처음 접하는 사용자에게 의미하는 바는 소스 코드가 위협적이지 않게 보일 것이라는 점이다. 따라서 줄당 가독성은 나쁜 설계 결정일지라도 좋은 마케팅 결정이 될 수 있다. 이는 사람들이 할부로 지불하게 하는 매우 성공적인 기술과 동형이다: 높은 선불 가격으로 그들을 겁주는 대신, 낮은 월별 지불액을 알려주는 것이다. 하지만 할부 계획은 구매자에게 순손실이며, 단순한 줄당 가독성도 프로그래머에게는 마찬가지일 것이다. 구매자는 그 낮은, 낮은 지불을 많이 할 것이고; 프로그래머는 개별적으로 읽기 쉬운 줄들을 많이 읽을 것이다.

이러한 상충 관계는 프로그래밍 언어보다 앞선다. 소설이나 신문 기사를 읽는 데 익숙하다면, 수학 논문을 처음 읽는 경험은 당황스러울 수 있다. 한 페이지를 읽는 데 30분이 걸릴 수도 있다. 하지만 나는 표기법이 문제가 아니라고 꽤 확신한다. 비록 그렇게 느껴질지라도 말이다. 수학 논문은 아이디어가 어렵기 때문에 읽기 어렵다. 만약 같은 아이디어를 산문으로 표현한다면 (수학자들이 간결한 표기법을 발전시키기 전에 그랬던 것처럼), 논문이 책 크기로 커질 것이기 때문에 읽기가 더 쉬워지지 않을 것이다.

어느 정도까지?

많은 사람들이 간결성 = 힘이라는 아이디어를 거부해왔다. 나는 단순히 같거나 같지 않다고 주장하는 대신, '간결성 = 힘'이 어느 _정도_인가를 묻는 것이 더 유용하다고 생각한다. 왜냐하면 간결성은 고수준 언어가 존재하는 목적의 큰 부분이기 때문이다. 만약 그것이 전부가 아니라면, 그 외에 무엇을 위한 것이며, 이러한 다른 기능들은 상대적으로 얼마나 중요한가?

나는 이 논쟁을 더 문명화하기 위해 이것을 제안하는 것이 아니다. 나는 정말로 답을 알고 싶다. 언어가 언제, 만약 그렇다면, 자신에게 너무 간결해지는가?

내가 시작했던 가설은 특수한 경우를 제외하고는 간결성을 힘과 동일하게 간주할 수 있다는 것이었다. 내가 의미한 것은 어떤 언어를 설계하든 그것들은 동일할 것이지만, 만약 누군가가 이 가설을 명시적으로 반박하기 위해 언어를 설계하고 싶다면, 아마 그렇게 할 수 있을 것이라는 점이었다. 사실, 나는 그것조차도 확신하지 못한다.

언어, 프로그램이 아니라

우리는 개별 프로그램의 간결성이 아니라 언어의 간결성에 대해 이야기하고 있다는 점을 분명히 해야 한다. 개별 프로그램이 너무 밀도 높게 작성될 수 있다는 것은 분명하다.

나는 온 리스프에서 이에 대해 썼다. 복잡한 매크로는 정당화되기 위해 자신의 길이의 몇 배를 절약해야 할 수도 있다. 만약 어떤 복잡한 매크로를 작성하는 것이 사용할 때마다 코드 10줄을 절약해줄 수 있고, 그 매크로 자체가 10줄의 코드라면, 한 번 이상 사용하면 순수하게 줄 수를 절약할 수 있다. 하지만 매크로 정의는 일반 코드보다 읽기 어렵기 때문에 여전히 나쁜 선택일 수 있다. 가독성에서 순수한 개선을 가져오려면 매크로를 10번 또는 20번 사용해야 할 수도 있다.

나는 모든 언어에 이러한 상충 관계가 있다고 확신한다 (비록 언어가 강력해질수록 위험이 커진다고 생각하지만). 모든 프로그래머는 어떤 영리한 사람이 의심스러운 프로그래밍 트릭을 사용하여 미미하게 짧게 만든 코드를 본 적이 있을 것이다.

따라서 그것에 대해서는 논쟁의 여지가 없다—적어도 나에게는. 개별 프로그램은 분명히 자신에게 너무 간결할 수 있다. 문제는 언어가 그럴 수 있는가이다? 언어가 프로그래머에게 전체적인 가독성을 희생하면서 짧은(요소 측면에서) 코드를 작성하도록 강요할 수 있는가?

언어가 너무 간결하다는 것을 상상하기 어려운 한 가지 이유는, 만약 어떤 것을 표현하는 지나치게 압축된 방법이 있다면, 아마도 더 긴 방법도 있을 것이기 때문이다. 예를 들어, 많은 매크로나 고차 함수를 사용하는 리스프 프로그램이 너무 밀집되어 있다고 느낀다면, 원한다면 파스칼(Pascal)과 동형인 코드를 작성할 수 있다. 만약 아크(Arc)에서 팩토리얼을 고차 함수 호출 (rec zero 1 * 1-)로 표현하고 싶지 않다면, 재귀적 정의를 작성할 수도 있다: (rfn fact (x) (if (zero x) 1 (* x (fact (1- x))))) 지금 당장 어떤 예시도 떠오르지 않지만, 언어가 너무 간결할 수 있는지에 대한 질문에 관심이 있다. 코드를 난해하고 이해하기 어려운 방식으로 작성하도록 강요하는 언어가 있는가? 만약 예시가 있다면, 매우 보고 싶다.

(참고: 내가 찾고 있는 것은 위에서 설명한 "요소" 측정 기준에 따라 매우 밀집된 프로그램이지, 단순히 구분자를 생략하고 모든 것이 한 글자 이름이라서 짧은 프로그램이 아니다.)