I thought I was building a C replacement. I was wrong

I thought I was building a C replacement. I was wrong

我以为我在构建一个 C 语言的替代品,但我错了

I made a rather fundamental mistake when I started marketing C3. I called it a C alternative. That seemed completely obvious to me. I had been looking for a better C for years, I found C2 to contribute to, and from that C3 was eventually born. Of course C3 was a C alternative. But I’ve gradually realized that the phrase “C alternative” means something very different today from what it meant to me. And the reason is embarrassingly simple: I’m old enough to remember when C was an application language. 当我开始推广 C3 时,我犯了一个相当根本的错误。我称它为 C 语言的替代品。这对我来说似乎是显而易见的。多年来我一直在寻找一种更好的 C 语言,我发现了 C2 并参与了贡献,C3 最终由此诞生。当然,C3 是 C 的替代品。但我逐渐意识到,“C 替代品”这个词在今天所代表的含义与我当初理解的截然不同。原因简单得令人尴尬:我年纪够大,还记得 C 曾经是一门应用开发语言。

So to understand what I got wrong, we have to go back to the 80s and 90s. I started in BASIC, and for the longest time, assembly and BASIC were the main options. This was during the home computer “revolution” of the 80s. With the advent of 16-bit home computers, more languages suddenly became viable — mainly Pascal and C. At that time, the most difficult thing was actually finding a compiler if you were young and had hardly any budget. I even ended up doing serious programming in QBasic of all things, simply because it was bundled with my install of DOS! And later I … ehem … “got hold” of a copy of Turbo Pascal… 为了理解我错在哪里,我们必须回到 80 年代和 90 年代。我从 BASIC 开始起步,在很长一段时间里,汇编和 BASIC 是主要的选择。那是在 80 年代家用电脑“革命”期间。随着 16 位家用电脑的出现,更多的语言突然变得可行——主要是 Pascal 和 C。在那个时候,如果你年轻且几乎没有预算,最困难的事情其实是找到一个编译器。我甚至最终用 QBasic 进行严肃的编程,仅仅因为它随我的 DOS 安装包附带!后来我……咳咳……“弄到”了一份 Turbo Pascal 的副本……

My Pascal days were ultra productive. If I wanted to do something, it was just a matter of sitting down and writing it. Compared to writing things with labyrinthine BASIC goto/gosub, Pascal was super nice and easy to organize. This was easy without imposing structure or architecture up front – after all, it was procedural. I did learn a smattering of C by getting hold of GCC, and later did some introductory C++ at university. And at this point, everything I wrote on my own was procedural in style. 我的 Pascal 时期效率极高。如果我想做什么,只需要坐下来写代码就行了。与用错综复杂的 BASIC goto/gosub 编写程序相比,Pascal 非常好用且易于组织。它不需要预先强加结构或架构——毕竟,它是过程式的。我通过获取 GCC 学习了一点 C 语言,后来在大学里学习了一些 C++ 入门知识。在那个阶段,我个人编写的所有代码都是过程式风格的。

Then I had a summer course in Java. I think this might have been 1996 and the internet was all new. And Java introduced OO as the core approach. It was novel, it was interesting, and despite having written C++, I felt I hadn’t really understood what OO was about until then. So that started my love affair with OO. Not that it was needed that much initially. Even getting a job in C++ later on gave me little room to do OO. But let’s fast forward. 后来我参加了 Java 的暑期课程。我想那可能是 1996 年,互联网还是个新鲜事物。Java 将面向对象(OO)作为核心方法引入。这很新颖,很有趣,尽管我写过 C++,但我感觉直到那时才真正理解什么是面向对象。于是,我开始了对面向对象的热爱。虽然最初并不怎么需要它。即使后来找到一份 C++ 的工作,也没有多少机会让我使用面向对象。但让我们快进一下。

I ended up being exposed to Objective-C, and a job on Java game servers gave me a massive amount of practice writing Java quickly and well, but I still felt a nagging dissatisfaction. Because I never really got the raw development speed I had in my early days with Turbo Pascal, I was much better at programming, I was somehow slower – thinking much more about design than before for equivalent code. And this was something I didn’t really reflect on that until I had a long-term contracting gig doing PHP. 我最终接触到了 Objective-C,一份 Java 游戏服务器的工作让我积累了大量快速且高质量编写 Java 的实践经验,但我仍然感到一种挥之不去的不满。因为我再也没有获得像早期使用 Turbo Pascal 那样的原始开发速度。我的编程水平提高了很多,但不知为何却变慢了——对于同样的代码,我比以前思考了更多的设计问题。直到我接了一份长期的 PHP 合同工作,我才真正反思这一点。

The codebase was somewhat OO, but not overly so. And the interesting thing was that, at its core, it was basically: “The user made a call, route it to running this function and present this result.” Even if bits and pieces inside were wrapped in OO classes, it was mostly procedural. And by god, development was ruthlessly efficient. Step by step it dawned on me that the OO parts were superfluous: – the whole thing could have been C with an arena allocator + good dynamic arrays and strings. No OO needed. 代码库带有一定的面向对象色彩,但并不严重。有趣的是,其核心本质上是:“用户发起调用,路由到执行该函数并呈现结果。”即使内部零星部分被封装在 OO 类中,它大部分仍是过程式的。天哪,开发效率简直高得惊人。我逐渐意识到,面向对象的部分是多余的:整个系统完全可以用 C 语言配合内存池分配器(arena allocator)加上优秀的动态数组和字符串来实现。根本不需要面向对象。

And that got me thinking. Here it was: the development speed of Turbo Pascal, so why didn’t OO give me that? I discovered an answer in OOPs requirement of up-front architecture. In OOP, you need to think about architecture from the first — what objects own what objects, what objects know of what objects. And a big part of the “best practices” in OO, such as programming to interfaces, are really ways of trying to mitigate the problem of this deep coupling. 这让我开始思考。这就是了:Turbo Pascal 的开发速度,为什么面向对象给不了我?我在 OOP 对预先架构的要求中找到了答案。在面向对象中,你需要从一开始就考虑架构——哪些对象拥有哪些对象,哪些对象了解哪些对象。而面向对象中很大一部分“最佳实践”,例如面向接口编程,实际上都是为了试图缓解这种深度耦合问题的方法。

The methods are fundamentally linked to this problem. As soon as we write: foo.do_something(bar) we have created a hierarchy where the class of foo is more fundamental than bar. In programming, we talk about exploring the problem space as we develop a program. As we gain a deeper understanding, we will usually restructure the program so that it more easily solves the problem. The problem with OOP — or, if I may spread the net wider, “methods first” — is that the up-front architecture and the use of methods inhibit these changes. 方法从根本上与这个问题相关联。一旦我们写下 foo.do_something(bar),我们就创建了一个层级结构,其中 foo 的类比 bar 更基础。在编程中,我们谈论在开发程序时探索问题空间。随着理解的加深,我们通常会重构程序,使其更容易解决问题。面向对象——或者如果我把范围扩大一点,即“方法优先”——的问题在于,预先架构和方法的使用抑制了这些变化。

When we have placed B in A, reversing that relationship doesn’t just mean moving the field: it also means rewriting all the methods that rely on it. This makes us reluctant to do such refactorings, which in turn means that bad decisions made up front tend to get locked in. This is not just a problem for OO, but for anything that is “methods first” — that is, when you think: game.run() instead of: run(&game). The latter is procedural thinking. The former is “methods first”. 当我们把 B 放入 A 中时,反转这种关系不仅仅意味着移动字段:它还意味着重写所有依赖于它的方法。这使得我们不愿进行此类重构,进而意味着早期做出的糟糕决策往往会被锁定。这不仅是面向对象的问题,也是任何“方法优先”模式的问题——也就是说,当你思考 game.run() 而不是 run(&game) 时。后者是过程式思维,前者是“方法优先”。

As an aside, C3 has methods because they are unreasonably effective for avoiding the need for function overloading in things like foo.to_string(). But this also makes it harder to drag people out of the “method first” mindset. (Here Odin has an advantage: it gets people into a good mindset from the start.) 顺便提一下,C3 拥有方法,因为它们在避免函数重载(例如 foo.to_string())方面有着不可思议的高效。但这同时也让人更难摆脱“方法优先”的思维定式。(在这方面 Odin 语言有优势:它从一开始就让人们进入良好的思维模式。)

For me, things were starting to come together. My PHP experience showed me that the OO parts were never actually needed to create nice abstractions. And the realization that OO — or more broadly, “methods first” — was making it harder to write good programs with good architecture gave me a better understanding of what I had been looking for all along. C wasn’t quite there, because libc was extremely bare-bones and overall didn’t have the necessary ergonomics anymore. 对我来说,事情开始变得清晰起来。我的 PHP 经验向我证明,创建良好的抽象根本不需要面向对象部分。而意识到面向对象——或者更广泛地说,“方法优先”——使得编写具有良好架构的优秀程序变得更加困难,这让我更好地理解了我一直以来在寻找的东西。C 语言并不完全符合要求,因为 libc 极其简陋,而且总体上不再具备必要的工程易用性(ergonomics)。

And yet a subset of C++ wasn’t the solution either. It was marinated in “methods first”, but above all, it was so slow to compile. So yes, I was looking for a C alternative. But what I was actually looking for was a C alternative with the ergonomics that made it suitable for current-day application development. And that distinction turned out to matter a lot. When I would say “C3 is a C-like for people who like C”, I was thinking about people who would write everything in C. People like Sean Barrett. But something I’ve gradually come to terms with is that “C-like” or “C alternative” today me… 然而,C++ 的子集也不是解决方案。它沉浸在“方法优先”中,最重要的是,它的编译速度太慢了。所以是的,我确实在寻找 C 的替代品。但我真正寻找的是一种具备现代应用开发所需易用性的 C 替代品。这种区别至关重要。当我曾说“C3 是为喜欢 C 的人准备的类 C 语言”时,我想到的是那些会用 C 编写一切的人。像 Sean Barrett 那样的人。但我逐渐意识到的一点是,今天的“类 C”或“C 替代品”……