Objective-C 到底给了程序员什么

不少程序员和我一样,对 Objective-C 经历了从反感到喜爱的转变。反感的是方括号语法和没有虚拟机 (virtual machine) 的动态语言实现。转变因为两个原因。一是 Objective-C 相对简单的语言构造。需要面向对象编程又惧怕 OOC 的程序员们终于摆脱了 C++ 的绑架,没有了跨函数的异常处理,没有了声东击西的操作符重载 (operator overloading) ,获得了统一的内存管理从而摆脱了 C++ 的 value copy 和 block-scope 语义(什么时候 copy constructor 会调用?什么时候 destructor 会调用?)。第二,尽管基于虚拟机的动态语言更强大,但完全脱离 C 还是不现实的,构建实际的软件需要 hybrid 编程,而没有虚拟机的 Objective-C 是一种「单一」语言,还是单一语言好用。

首先谈谈第一点,Objective-C 比 C++ 简单是否仅仅限于砍掉了一堆 C++ 语言的 feature 而已?那么又增加了方括号语法是不是在「砍掉 feature」大基调下一个不和谐的杂音?

范式分割

C++ 被其设计者 Bjarne Stroustrup 定义为「多范式 (multi-paradigm) 的通用编程语言」[1]。Stroustup 的基本出发点看似没有什么问题,解决不同的问题,尤其是不同层次的问题,需要不同范式的编程语言。但是,能不能把不同范式揉合到一种语言当中呢?至少 C++ 的具体方式很糟糕。我认为正确的方式是让程序员在(面向不同问题,或者分析不同代码的)不同时候用不同范式思考,提供在范式之间切换的手段。尽量让程序员在每个时刻只需用单一范式思考。同时混用多个范式是巨大的智力负担。C++ 正好反其道行之:对所有范式都选取相似的语法符号,更糟糕的是为了加大相似度特地提供了操作符重载。Stroustrup 在自己的书里多次提到他的出发点是让所有范式的所有元素都成为 first-class citizen。这是个似是而非的肤浅观点。平等的成为 first-class citizen 并不的意味着被相似的符号表示。C++ 不仅鼓励没有节制的混用范式,即使某段代码实际上只采用单一范式,维护它的程序员也无法放心的排除其它范式。结果是对任何看似简单的代码都必须同时考虑 C++ 的所有范式。

应该说这个问题并非 C++ 独有。高级动态语言同样可能在没有明显代码风格差异的情况下改变范式。但我在之前的 blog 里讨论过 [2],丰富表现力的子语言解决复杂单一的问题的可以采用这种方式,却并非适合团队协作的通用编程语言应该采用。

Objective-C 和 C++ 相比无论语言元素是增是减,始终为了一个目的,向程序员清晰的表达目前所处的范式。首先它的目标更简单,没有难以融合的 generic 范式,集中精力实现 OO 范式。它提供了方括号语法明确表示范式的切换。排除了操作符重载,避免混淆范式。没有跨函数的异常处理,避免破坏 C 的过程范式的重要部分,也是起系统胶合剂作用的 ABI 重要部分 —— call stack 规范。所以,当程序员看到一段 Objective-C 代码时,他可以根据方括号的出现与否轻松的决定是否让头脑卸下思考 OO 动态行为的负担,集中注意力到 C 的静态部分;还是让头脑卸下思考内存管理等底层问题的负担,用 OO 的高级抽象思考问题。

Objective-C 的设计者 Tom Love 解释过方括号语法确实是为了让 C 部分和 OO 扩展部分成为有鲜明区别的「两个」语言 [3]。虽然 hybrid 编程会带来的互操作和调试的负担,但是程序员都喜欢用不同范式解决不同层次的问题来减轻头脑的负担。适合问题的范式和范式之间明确的界限是 hybrid 编程最大的优势。

工具融合

如果 Objective-C 是两种语言的 hybrid 编程。那么一开始提到,和其它高级动态语言与 C 语言的 hybrid 编程相比,Objective-C 是一种「单一」语言又该怎么理解?这取决于工具支持。Objective-C 在语言设计方面保持两个独立的语言定义,又用工具把实际使用中可以融合的部分尽量融合到一起。

首先,hybrid 编程依靠不同的语法来区分范式。大多数 hybrid 编程把针对不同语法的编译器也区分开来。比如,Lua 的编译器是一个 library,和 C 编译器没有任何联系。参数通过 API 传递。Objective-C 的做法是提供可以同时处理两种语言的单一编译器。虽然揉合了两种语言的处理,但是可以设想,除去前端 parser 的语法树 (AST) 产生可能复杂一些,这个编译器的后端处理不会比单独写两个编译器更复杂。和 C++ 融合多种范式的编译器复杂度不可相提并论。对程序员来说,Objective-C 的源文件作为一个整体是极大的便利。

不论范式如何不同(摧残头脑的 functional 编程除外),程序的运行都是线性的,所以每个线程中包括所有范式语言的单一 call-stack 是存在的。尽管逻辑上可行,许多 hybrid 编程环境里并不提供能展示单一 call-stack 的调试工具。如果需要,程序员只能自己用头脑分析和想像。Objective-C 极大的改进了这点。除统一的编译器之外,让 Objective-C 成为「单一」语言的另一个要素是能集成分析 C 和 OO 扩展部分运行状态的调试器。

从工具集合来看,Objective-C 比大多数开源语言都丰富。无论开源还是商业领域,直接支持 hybrid 编程的单一编译器和展示 hybrid 运行时的单一调试器都是 Objective-C 独特的优势。

共生系统

Objective-C 体现了独特但合理的设计编程环境的思路:使用同一工具的并不非得是一个语言,多个语言也不见得非要使用多个分立的工具。集成不同语言的可以是方便的工具而不是脆弱的 API。对两种定义清晰的语言提供高度集成的同一工具是 Objective-C 比很多晚出现十几年的语言更显先进的原因。太多语言设计者抱着这样的错误成见:我的语言必须是一个整体;我的语言可以令程序员从开始 coding 一刻起就避免和解决很多其它语言需要用工具解决的问题,所以不需要太多工具;我的语言这么好所以会有无数的人为它写工具,我只要专心设计语言最小核心的编译解释工具。我曾经写过 Lua 最大限度避免了集成污染 [4],因为 Lua 利用了 C 编译器对 ANSI C 的良好支持。遗憾的是,在这个利用工具的良好开端之后,和其它语言一样,Lua 的开发团队始终是限于语言本身的改进而没有任何附带的增强工具。

编程环境是语言和工具的共生体,但是人们经常忽视这个事实,或者只是关心共生体的一小部分,徒劳的希望别人来改进其它部分。Mac OS X 的编程环境,得益于 Apple 这家强于 end-to-end control [5] 的公司选择了独特的 Objective-C。

脚注:

  1. Design and Evolution of C++》。
  2. 高级动态语言和软件业》,《高级动态语言和软件业 —— 交流与内省》。
  3. 《Masterminds of Programming》。
  4. 集成污染
  5. Objective-C 说明了 end-to-end control 并不等同封闭。它的实现 Clang 和 LLVM/LLDB,乃至之前的 GCC 都是 open source 实现。Control 只是推动了它们的整体发展和最终集成。Apple 选择了工具支持最好的 Objective-C,同时在业界对其缺乏兴趣的环境下一力发展 Objective-C 的语言和工具是有勇气的。

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google+ photo

You are commenting using your Google+ account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s


%d 博主赞过: