C3를 C 대체재로 설명한 것이 왜 근본적인 오해였는지, 그리고 C3가 지향하는 현대적 범용 애플리케이션 개발 언어의 역할을 돌아본다.
C3를 홍보하기 시작했을 때, 나는 꽤 근본적인 실수를 했다.
나는 그것을 C 대안이라고 불렀다.
내게는 그게 완전히 자명해 보였다. 나는 수년간 더 나은 C를 찾고 있었고, 기여할 수 있는 C2를 발견했으며, 결국 그로부터 C3가 태어났다. 물론 C3는 C 대안이었다.
하지만 나는 점차 오늘날 “C 대안”이라는 표현이 내게 의미했던 것과는 완전히 다른 뜻을 가진다는 사실을 깨달았다.
그 이유는 민망할 정도로 단순하다. 나는 C가 애플리케이션 언어였던 시절을 기억할 만큼 나이가 들었다. 따라서 내가 무엇을 잘못 이해했는지 알려면 80년대와 90년대로 돌아가야 한다.
나는 BASIC으로 시작했고, 아주 오랫동안 어셈블리와 BASIC이 주된 선택지였다. 이는 80년대 가정용 컴퓨터 “혁명”이 일어나던 시기였다. 16비트 가정용 컴퓨터의 등장으로 더 많은 언어가 갑자기 실용적인 선택지가 되었는데, 주로 Pascal과 C였다.
당시 가장 어려운 일은 젊고 예산이 거의 없는 사람이 컴파일러를 구하는 일이었다. 나는 심지어 DOS 설치본에 함께 들어 있었기 때문에, 별것 아닌 QBasic으로 진지한 프로그래밍을 하게 되었다! 그리고 나중에는… 흠… Turbo Pascal 사본을 “구하게” 되었다…
Pascal을 쓰던 시절은 엄청나게 생산적이었다. 무언가 하고 싶다면, 그냥 앉아서 작성하면 되었다. 미로 같은 BASIC의 goto/gosub로 무언가를 작성하는 것과 비교하면 Pascal은 무척 깔끔하고 정리하기 쉬웠다. 절차형 언어였으므로 사전에 구조나 아키텍처를 강요하지 않으면서도 이것이 쉬웠다.
나는 GCC를 구해서 C를 조금 배웠고, 나중에는 대학에서 기초적인 C++도 공부했다. 그리고 이 시점까지 내가 혼자 작성한 것은 전부 절차형 스타일이었다. 그러다 여름에 Java 강좌를 들었다. 아마 1996년이었을 것이고, 인터넷은 완전히 새로운 것이었다.
그리고 Java는 객체 지향을 핵심 접근법으로 제시했다.
그것은 새롭고 흥미로웠으며, C++를 작성해 본 적이 있었음에도 그때까지는 객체 지향이 무엇인지 정말로 이해하지 못했던 것 같았다. 그렇게 객체 지향과의 애정 관계가 시작되었다. 처음에는 그것이 그렇게 많이 필요했던 것은 아니다. 나중에 C++로 취직했을 때도 객체 지향을 할 여지는 거의 없었다. 하지만 시간을 훌쩍 넘겨 보자.
나는 Objective-C를 접하게 되었고, Java 게임 서버에서 일하면서 Java를 빠르고 잘 작성하는 연습을 엄청나게 하게 되었다. 하지만 여전히 끈질긴 불만이 남아 있었다. 초창기 Turbo Pascal 시절에 누렸던 순수한 개발 속도를 결코 얻지 못했기 때문이다. 나는 프로그래밍을 훨씬 더 잘하게 되었지만, 비슷한 코드를 작성하는 데 예전보다 훨씬 더 디자인을 고민하면서 어쩐지 더 느려졌다. 그리고 PHP로 장기 계약 일을 하기 전까지는 이를 제대로 되돌아보지 못했다.
그 코드베이스는 어느 정도 객체 지향이었지만 지나치지는 않았다. 그리고 흥미로운 점은, 그 핵심이 기본적으로 다음과 같았다는 것이다.
“사용자가 호출했다. 이를 이 함수를 실행하는 곳으로 라우팅하고 이 결과를 보여 준다.”
내부의 일부가 객체 지향 클래스에 감싸여 있기는 했지만, 대부분은 절차형이었다. 그리고 정말이지 개발은 가차 없을 정도로 효율적이었다.
조금씩 객체 지향 부분이 불필요하다는 사실이 깨달아졌다. 즉, 전체는 아레나 할당기와 좋은 동적 배열 및 문자열을 갖춘 C로 만들 수 있었다. 객체 지향은 필요 없었다.
그것이 나를 생각하게 만들었다. 바로 이것이었다. Turbo Pascal의 개발 속도 말이다. 그렇다면 객체 지향은 왜 그것을 주지 못했을까? 나는 객체 지향이 사전 아키텍처를 요구한다는 점에서 답을 발견했다.
객체 지향에서는 처음부터 아키텍처를 생각해야 한다. 어떤 객체가 어떤 객체를 소유하는지, 어떤 객체가 어떤 객체를 아는지를 말이다. 그리고 인터페이스를 대상으로 프로그래밍하는 것과 같은 객체 지향의 “모범 사례” 상당수는 사실 이러한 깊은 결합 문제를 완화하려는 방법이다.
메서드는 근본적으로 이 문제와 연결되어 있다. 우리가 다음과 같이 작성하는 순간,
foo.do_something(bar)
foo의 클래스가 bar보다 더 근본적인 계층 구조를 만들어 낸다.
프로그래밍에서는 프로그램을 개발하며 문제 공간을 탐색한다고 말한다. 더 깊이 이해하게 되면, 대개 문제를 더 쉽게 해결하도록 프로그램을 재구성하게 된다.
객체 지향의 문제, 혹은 범위를 조금 더 넓혀 “메서드 우선”의 문제는 사전 아키텍처와 메서드 사용이 이러한 변화를 방해한다는 것이다. B를 A 안에 배치한 뒤 그 관계를 뒤집으려면 필드를 옮기는 것만으로는 안 된다. 그것에 의존하는 모든 메서드도 다시 작성해야 한다. 이는 우리가 그러한 리팩터링을 꺼리게 만들고, 결국 처음에 내린 나쁜 결정이 고착되는 경향으로 이어진다.
이는 객체 지향만의 문제가 아니라 “메서드 우선”인 모든 것의 문제다. 즉, 다음처럼 생각할 때다.
game.run()
다음처럼 생각하는 대신 말이다.
run(&game)
후자는 절차형 사고다. 전자는 “메서드 우선”이다.
덧붙이자면, C3에 메서드가 있는 이유는 foo.to_string() 같은 경우에 함수 오버로딩이 필요하지 않게 해 주는 데 비합리적일 만큼 효과적이기 때문이다. 하지만 이는 사람들을 “메서드 우선” 사고방식에서 끌어내는 일을 더 어렵게 만들기도 한다.
(이 점에서 Odin은 장점이 있다. 처음부터 사람들을 좋은 사고방식으로 이끌기 때문이다.)
내게는 여러 조각이 맞춰지기 시작했다.
PHP 경험은 멋진 추상화를 만들기 위해 객체 지향 부분이 실제로는 전혀 필요하지 않았음을 보여 주었다. 그리고 객체 지향, 더 넓게는 “메서드 우선”이 좋은 아키텍처를 갖춘 좋은 프로그램을 작성하기 어렵게 만든다는 깨달음은, 내가 그동안 줄곧 찾고 있던 것이 무엇인지 더 잘 이해하게 해 주었다.
C는 정확히 그 지점에 있지 못했다. libc는 극도로 기본적인 기능만 제공했고, 전반적으로 더는 필요한 사용 편의성을 갖추지 못했다. 그렇다고 C++의 부분집합도 해답은 아니었다. 그것은 “메서드 우선”에 흠뻑 젖어 있었고, 무엇보다 컴파일이 너무 느렸다.
그래서 그렇다. 나는 C 대안을 찾고 있었다.
하지만 내가 실제로 찾고 있던 것은 오늘날의 애플리케이션 개발에 적합하도록 사용 편의성을 갖춘 C 대안이었다. 그리고 이 구분은 매우 중요하다는 것이 드러났다.
내가 “C3는 C를 좋아하는 사람들을 위한 C 계열 언어”라고 말할 때, 나는 모든 것을 C로 작성할 사람들을 생각하고 있었다. Sean Barrett 같은 사람들 말이다.
하지만 내가 점차 받아들이게 된 것은, 오늘날 “C 계열” 또는 “C 대안”이 다소 다른 뜻을 가진다는 사실이다. 이는 “오늘날 C가 주로 사용되는 일을 위한 프로그래밍 언어”라는 뜻이다. 즉 운영체제 개발, 임베디드, 고성능 틈새 라이브러리와 백엔드를 의미한다.
사람들은 데이터베이스를 C로 작성하지, 다음 동영상 편집기나 게임을 C로 작성하지는 않는다. 그런 용도에는 C++나 그와 비슷한 것을 사용한다. C++를 사용하면 C에 없는 동적 문자열과 배열, 맵, 약간의 오버로딩 같은 추가적인 사용 편의성을 얻을 수 있기 때문이다. 그리고 그것은 완벽하게 타당하다.
하지만 내가 생각한 것은 그것이 아니었다. 나는 프로그램을 작성하는 언어로서의 C를 생각하고 있었다. 범용 애플리케이션 말이다. 사람들이 이제 C++, Objective-C, Swift, Java 또는 Kotlin으로 만드는 종류의 것들 말이다.
나는 항상 C3가 그러한 애플리케이션을 작성하기에 똑같이 좋거나 더 좋다고 생각했다. 이는 C3를 또 다른 C++, 또 다른 Java, 또 다른 Swift로 만들고 싶어서가 아니다. 정반대다. 나는 C의 순수한 단순성과 성능을 원했지만, 현대 소프트웨어를 즐겁게 만들기 위해 필요한 사용 편의성도 갖추기를 원했다. 충분한 사용 편의성을 갖춰 일을 끝내는 데 가능한 한 즐겁고 편리하게 쓸 수 있는 언어, 그저 그런 언어를 원했다.
오늘날 대부분의 프로그래머가 보기에 이것은 C와 연관 짓는 것이 아니다. 하지만 내게는 C와 연관된 것이었다. 내가 기억할 만큼 오래전에는 그것이 실제로 C의 역할이었기 때문이다.
(그리고 이것이 Zig의 결정이 C3의 결정과 크게 다른 이유이기도 하다. Zig의 주된 목표는 오늘날 C가 사용되는 곳에서 C를 대체하는 것처럼 보인다.)
사람들이 “사람들은 왜 C를 대체하고 싶어 하나?”라고 말할 때, 나는 항상 그것을 잘못 이해했다.
“C면 충분히 좋다. 대체재는 필요 없다.”
하지만 그들이 실제로 뜻한 것은 다음과 같았다.
“C는 흥미로운 일에 쓰이지 않는데, 왜 굳이 대체재를 만들지?”
그리고 바로 그 지점에서 나는 논의를 오해하고 있었다. C가 다른 것을 의미하는 세상에서, 나는 “C 대안”의 오래된 정의를 쓰고 있었다. 그러므로 C3를 C 대안으로 홍보했을 때, 나는 꽤 근본적인 실수를 저질렀다.
C3는 언제나 C부터 C++, Swift까지 모든 것의 대안이었다. 나는 세상이 얼마나 많이 변했는지 충분히 생각하지 못했을 뿐이다. 그리고 그것은 내가 C3를 어떻게 이야기해야 하는지를 바꾼다.
그러므로 앞으로 C3는 C 계열 언어라는 이야기를 덜 하고, 범용 애플리케이션을 구축하기 위한 프로그래밍 언어라는 이야기를 더 하게 될 것이다. 요점은 언제나 다음과 같았기 때문이다.
C를 새롭게 다듬어 다시 즐거운 범용 애플리케이션 언어로 만드는 것.
다시 말해, 나는 수년간 C3를 C 대체재로 홍보했지만, 실제로 만들려고 했던 것은 훨씬 더 폭넓은 것이었다.
나는 그저 그것을 잘못 홍보했다.
이 블로그 게시물은 Reddit, Lobste.rs 또는 HackerNews에서 토론할 수 있다.