Carbon Language 设计原则详解:为何坚持“一件事只做一种方式“的 One-Way 原则
2026/9/9 23:26:55 网站建设 项目流程

Carbon Language 设计原则详解:为何坚持"一件事只做一种方式"的 One-Way 原则

【免费下载链接】carbon-langCarbon Language's main repository: documents, design, implementation, and related tools. (NOTE: Carbon Language is experimental; see README)项目地址: https://gitcode.com/GitHub_Trending/ca/carbon-lang

Carbon 语言项目把"为同一件事只提供唯一一种写法"确立为正式的语言设计原则(Principle),记录于 docs/project/principles/one_way.md。这一原则直接约束 Carbon 相对 C++ 的语法取舍:逻辑运算符只保留and/or/not文本形式、十六进制字面量只允许0xAA大小写、structclass只保留一个、控制流必须使用大括号等。本文基于该原则文档及其背后的提案 proposals/p000829-one-way-principle.md 完整梳理:为什么多重写法会成为问题、该原则在哪些具体语法决策上落地,以及它明确承认的三类例外,并结合toolchain源码说明这些取舍在实现层是如何体现的。

原则定位:一份"决策工具",而非功能设计

在 docs/project/principles/ 目录下,Carbon 集中收录了对多个设计都有影响的"语言设计原则"。按照 原则目录说明,原则与单个功能设计的关键区别在于:一条原则应当指导多个功能的设计,而单个功能设计通常只服务于特定目标。原则的作用是在这些设计中维持一致性,并帮助贡献者理解决策是如何做出的。原则会明确项目"想要追求"和"想要排除"的两类做法,且它澄清但不凌驾于语言目标(goals)之上。

One-Way 原则正是这样一份决策工具,其完整表述是:在 Carbon 中,我们优先只提供完成某件事的唯一方式——即当某个语法场景存在多个等价的设计选项时,倾向于只提供一个选项,而不是提供多个选项让用户选择。文档特别指出,这与 Python 的著名立场一脉相承:

"There should be one -- and preferably only one -- obvious way to do it."(PEP 20)

与之相对的是 Perl 的 "There is more than one way to do it"(TMTOWTDI)。Carbon 在提案 p000829 中把"允许多种等价写法"列为一个正式考虑的替代方案,但最终明确拒绝,理由正是"我们珍视通过最小化功能重叠带来的语言简洁性"。

背景:多重写法为何是真实的设计成本

原文档的 Background 一节系统性地论证了"一件事多种做法"的三个来源与代价,这也是理解本原则的起点。

来源一:语言演进的遗留。语言为了向后兼容而难以移除旧写法,于是新旧写法并存。

来源二:有意提供冗长与简洁两种版本。同一语法同时提供 verbose 与 concise 两种形式。

由此衍生出的代价包括:

  • 选择悖论(Paradox of Choice):当多个选项相似到足以让开发者花时间去分析权衡时,花费的分析时间可能超过做出正确选择的潜在收益。文档明确引用了这个现象作为担忧对象。
  • 风格指南的失效:多个相似选项并存时,往往要靠风格指南指定"首选"——有时是因为某个选项客观上更好,有时仅仅是"做出选择总比不做好"。即便有风格指南,开发者仍可能因偶然或刻意而风格分叉:两个写法都能用,于是大家各选一种;开发者跨组织流动时,还要重新学习和重塑习惯。
  • 工具链负担:从提案 p000829 的"替代方案"论证可以看到,若接受多重写法,"构建语法解析与拼写纠错(typo correction)的难度会上升,因为需要纠正的选项更多,而且在给定上下文中可能多个都是合法的"。这一点直接关系到toolchain中解析与诊断代码的复杂度。

原则带来的三项具体收益

文档将"最小化选择"的收益明确挂钩到 Carbon 的语言目标上(均指向 docs/project/goals.md 中的对应小节):

  1. 语言工具更易编写与维护(对应"语言工具与生态系统"目标):功能重复度降低意味着语言复杂度降低,工具实现随之简化。
  2. 软件与语言演进更容易推进:演进过程既能更容易地审视既有语法,也能避免产生新的语法冲突。
  3. 代码更易理解:开发者需要掌握的语法更少,在最终代码结构不过于复杂的前提下,可以预期提升代码质量与生产力。

一句话概括文档的结论:通过最小化语言功能的重叠,Carbon 希望同时让自己的维护者和开发者都更轻松。

应用实例:与 C++ 对比的四个取舍

文档"Applications of this principle"一节给出了四个典型应用,其中三个以"提升可理解性"为首要动机,一个以"语言工具简化"为首要动机。这些取舍在仓库中都能找到对应的提案文档和源码证据。

1. 逻辑运算符:只有文本形式and/or/not

C++ 中逻辑运算符既可用符号(&&||!)也可用文本(and等)。Carbon 只保留文本形式,对应提案 p000680-and-or-not.md。该提案的"Rationale based on Carbon's goals"与"Alternatives considered"中专门讨论了"全部使用标点拼写"等替代方案并予以否决。

在工具链源码中,解析器为这些运算符定义了专用的解析节点种类,见 toolchain/parse/node_kind.def:

CARBON_PARSE_NODE_KIND_EXPRESSION(ShortCircuitOperandAnd) CARBON_PARSE_NODE_KIND_EXPRESSION(ShortCircuitOperandOr) CARBON_PARSE_NODE_KIND_EXPRESSION(ShortCircuitOperatorAnd) CARBON_PARSE_NODE_KIND_EXPRESSION(ShortCircuitOperatorOr)

以及前缀取反节点 CARBON_PARSE_NODE_KIND_PREFIX_OPERATOR(Not)。从源码结构看,解析树中只有 And/Or/Not 这一套短路求值节点,不存在符号形式的平行节点,印证了"单一写法"在解析实现层就是单一实现路径——没有为第二种拼写预留任何分支。

2. 十六进制字面量:只允许0xAA大小写

C++ 的十六进制字面量中,0xaa0xAA0xAa乃至前缀大写0XA均合法。Carbon 依据提案 p000143-numeric-literals.md 只允许0xAA这一种大小写组合。

该规则在词法层有直接体现:toolchain/lex/character_set.h 中定义了专门的IsUpperHexDigit字符判定函数:

inline auto IsUpperHexDigit(char c) -> bool { ... }

其命名(Upper 而非 Hex)本身就说明 Carbon 词法器只识别"大写十六进制数字"这一唯一形态。在字符串字面量的转义序列中同样贯彻了这一点,例如 toolchain/lex/string_literal.cpp 中要求\x后跟两个大写十六进制数字(\x0F),并要求\u{70AD}形式使用大写字母——诊断信息中甚至直接把"uppercase hexadecimal digits"写进了报错文案。

3.structclass:只保留class

C++ 同时提供structclass,二者唯一区别是成员可见性的默认值。Carbon 只提供class(且默认 public 可见性与 C++ 的struct不同)。这意味着解析器中不需要为两种类型引入器维护几乎相同的解析逻辑,也避免了"该用 struct 还是 class"的风格之争。

4. 控制流:大括号不可省略(工具简化型动机)

与前三例不同,这一条的主要动机是语言工具。C++ 允许单语句控制流省略大括号,Carbon 依据提案 p000623-require-braces.md 要求大括号不可省略。该提案指出:省略大括号会让语法歧义更多(例如else的归属、语句边界判断),增加错误检测难度;提案还援引了 Apple 著名的goto fail漏洞作为反面背景。从 One-Way 原则视角看,"可以省略也可以不省略"本身就是同一种控制流结构的两种写法,强制统一为"总是有括号"消除了一个解析与错误恢复都更复杂的分支。提案同样允许else if作为连续if的专用语法糖,因为它的出现频率足够高。

三类明确的例外(Caveats)

One-Way 原则不是教条。文档用专门的 Caveats 一节划定了三种原则可以放宽的情形,这是使用该原则做设计判断时必须读到的部分。

例外一:专门化语法(Specialized syntax)

当某种专门语法为常见用例特别复杂且重要的用例带来显著收益时,允许重叠出现。文档列出三种情形:

  • 性能:有时必须提供比通用语法更能支撑优化的专门语法(对应"性能关键软件"目标)。
  • 可理解性:某个用例足够普遍时,简化其语法收益很大。文档给出的例子是for (var x: auto in list)——它本来总可以用while循环改写,但基于范围的 for 循环被认定为提升可读性;反之,C++ 的for (;;)while足够接近,因此预期用while覆盖其用例,不再单设一种写法。
  • 迁移与互操作:有时务实的做法是同时提供"新 Carbon 代码的理想方式"和"更兼容 C++、便于迁移的方式"。文档给出的关键例子是泛型(generics)与模板(templates):泛型是新代码的首选形式,但模板是 C++ 代码迁移的必要条件。文档特别强调"这不是一个演进情形,因为我们不预期会移除模板"——这为"两种机制长期并存"做了预先澄清,避免它被误读为原则的违例。

例外二:非显见的替代方式(Non-obvious alternatives)

呼应 Python 的立场:某些替代写法虽然存在,但足够非惯用(non-idiomatic),因此不会在实际代码中常见。文档的例子:

  • while (condition) { DoSomething(); break; }替代if (condition) { DoSomething(); }
  • 用其他代码构造去实现 lambda 的功能——代码量显著更多且损害可读性。

文档给出的判定标准是:如果两种写法之间的选择不会主要基于编码风格,说明它们差异已经足够大,这一原则就不适用了。

例外三:演进过程中的暂时并存(In evolution)

为支持演进,语言常常需要临时同时提供"旧"与"新"两种写法。例如重命名某个语言特性时,可以短期内在两个标识符下提供相同功能,但其中一个必须标记为废弃(deprecated)并计划移除。文档特别警告:应当警惕新增重叠功能却不附带移除对应遗留版本的计划——临时并存必须收敛,不能变成事实上的永久双轨。

被正式拒绝的替代方案:Perl 式多重写法

提案 p000829 完整记录了"允许多种写法"这一替代方案的内容与被拒理由,值得设计者完整了解,因为它列出了如果放弃 One-Way 原则会引入哪些具体语法:

  • 为匹配 C++ 遗留:引入for (;;)、重新允许if等语句省略大括号、同时支持带 C++ 默认可见性规则的classstruct、同时支持and&&0xa0XA等;
  • ;变为可选(现代语言有此先例);
  • 模板与泛型做成特性对等(feature parity),让两者都能很好地解决泛型编程问题。

其优点是:开发者更可能找到自己喜欢的语法;可以主动支持一种"C++ 方言"从而降低迁移成本。缺点则是:

  • 开发者要么接受个人风格,要么催生更多风格指南——无论个人层面还是组织层面,这都等于制造语言方言;
  • 增加语法解析与拼写纠错的构建难度,因为在同一上下文中可能有多个候选写法都合法。

提案还提醒不要把这个替代方案理解到极端:Perl 还有句相关箴言 "There is more than one way to do it, but sometimes consistency is not a bad thing either"(一致性也不坏)。极端例子是把extensibleextendableextendible三个词都作为等价关键字提供——这种收益极低(尤其extendableextendible之间)的分叉不应被接受。

如何使用这一原则做设计判断

综合原则文档与提案,可以提炼出一个可操作的判断流程,这也是 原则目录 所期望的"用原则作为决策工具"的具体化:

  1. 当一个新语法场景出现多个候选写法时,先问:它们是否只是风格差异?若是,只保留一个(如andvs&&0xAAvs0xaa)。
  2. 若候选之一服务于性能优化或高频用例的可读性,且收益显著,可允许专门化语法(如 range-based for),但要确认它与通用写法的距离足够远,避免for(;;)vswhile这类"近邻重叠"。
  3. 若候选来自 C++ 迁移需求,可短期或长期并存,但必须写清楚它是迁移工具而非通用选项(如 templates)。
  4. 若为演进而引入新写法,必须为旧写法规划废弃与移除路径,否则违反"临时并存"的前提。
  5. 若两种写法差异大到选择不会基于风格(如 lambda vs 手工展开),则本原则根本不适用,无需强行二选一。

小结

Carbon 的 One-Way 原则(docs/project/principles/one_way.md)把"一件事只提供唯一写法"从一句口号变成了有边界的设计工具:它以语言工具简化、演进友好和代码可读性三大收益为论据,用 C++ 对照给出了and/or/not0xAA、单一class、强制大括号四个落地实例(分别见 p000680、p000143、p000623),同时用专门化语法、非惯用替代与演进并存三类 caveat 划定了原则的适用边界,并在 p000829 中正式记录并拒绝了 Perl 式多重写法方案。对参与 Carbon 设计评审或阅读其工具链源码(如 toolchain/parse/node_kind.def、toolchain/lex/character_set.h)的开发者来说,这条原则解释了为什么解析树与词法判定中"只有一条路径":不是实现偷懒,而是语言层面就不允许第二条路径存在。

【免费下载链接】carbon-langCarbon Language's main repository: documents, design, implementation, and related tools. (NOTE: Carbon Language is experimental; see README)项目地址: https://gitcode.com/GitHub_Trending/ca/carbon-lang

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询