免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Carbon 语言设计原则:以 interface 作为唯一静态开放扩展机制

Carbon 语言设计原则:以 interface 作为唯一静态开放扩展机制 Carbon 语言设计原则以 interface 作为唯一静态开放扩展机制【免费下载链接】carbon-langCarbon Languages main repository: documents, design, implementation, and related tools. (NOTE: Carbon Language is experimental; see README)项目地址: https://gitcode.com/GitHub_Trending/ca/carbon-lang导读本文解读 Carbon 语言的一项核心设计原则——One static open extension mechanism单一静态开放扩展机制。该原则确立于提案 proposals/p000998-principle-one-static-open-extension-mechanism.md并由 docs/project/principles/static_open_extension.md 作为正式原则文档沉淀下来。它回答了一个关键问题在 Carbon 中开发者如何在不改动既有类型定义的前提下为类型扩展运算符、swap等操作答案是——interface是唯一的静态开放扩展机制。读完本文你将理解该原则提出的背景C 开放函数重载的弊端、具体约束函数重载被限制在同一库内、对泛型检查与 C 互操作的影响以及它如何支撑 Carbon代码易读、易理解、易编写的语言目标。一、背景为什么需要开放扩展以及 C 的做法开放扩展open extension指的是为类型库之外的代码提供一种途径让该类型可以参与新的操作——典型场景是为运算符定义重载例如让任意类型支持、、等。该提案特别关注那些支持**静态分发static dispatch**的开放扩展方案因为 Carbon 的首要目标是 性能关键软件需要避免动态分发的运行时开销。业界主流的三种候选方案是方案代表语言/机制基本思想开放函数重载open function overloadingC一个名字可以在多处新增重载定义调用点按参数匹配接口interfaces / traitsRust类型通过显式实现接口来声明能力特殊方法名special method namesCoperator关键字、Python双下划线方法__add__等用约定俗成的名字让运算符与普通函数挂钩提案的立场非常明确优先选择单一机制以保持语言简单。最终结论是采用 interfaces。C 开放函数重载的两个根本问题原则文档 docs/project/principles/static_open_extension.md 详细剖析了 C 以函数重载作为扩展点带来的两个问题第一重载签名不受约束泛型代码无法独立类型检查。在 C 中一个函数可以被重载定义分散在多个文件中ADL实参依赖查找 甚至允许无限定调用解析到不同命名空间里的函数。这些规则被用来定义具有静态分发的扩展点典型如运算符重载和std::swap。但是 C 对函数重载的签名没有任何约束——当用重载作为扩展点、为多种类型定义同一操作时就没有办法对试图在这些类型上调用该操作的泛型代码做类型检查。模板代码只有在实例化时才能验证某个类型是否真的提供了所需的重载错误会被推迟到很远的地方才暴露。第二没有直接、简单的退出机制。在 C 中只要非成员函数与调用参数列表可能关联的类型处于同一命名空间它就可能被 ADL 找到。函数声明层面没有直接的退出手段虽然调用点存在退出手段但很少被使用。其后果是大量同名非成员函数即使功能毫不相关也会被并入同一个重载集合代码中没有任何信息能表明不同命名空间里的哪些函数有意暴露同一能力。也就是说把碰巧同名误当成表达同一操作是很容易发生的意外这正是开放重载缺乏显式意图表达的体现。此外开放重载具有上下文敏感性同一个名字解析成什么取决于当前导入了哪些内容。这与 Carbon 的 低上下文敏感原则 直接冲突。二、原则interfaces 是 Carbon 唯一的静态开放扩展机制原则文档给出的一句话表述即核心In Carbon,interfaces are the only static open extension mechanism. 在 Carbon 中interface是唯一的静态开放扩展机制。围绕这句话可以拆解出以下几层含义2.1 每个类型可以为每个接口定义自己的实现interface描述一组函数/方法的签名契约每个类型都可以显式实现它。泛型代码可以针对任何实现了该接口的类型编写并且可以独立于泛型被实例化的具体类型进行类型检查——依据就是接口规定了调用的签名。这正是 docs/design/generics/overview.md 中definition checking定义检查能力的来源泛型定义的检查只依赖签名契约不依赖调用点信息。接口的实现有两种书写位置具体语法见 generics 总览文档中的示例interface Printable { fn Print(self); } class Song { // 在类定义内使用 extend impl接口成员并入类 API extend impl as Printable { fn Print(self: Song) { ... } } } // 在类定义外所在库使用独立 impl 声明 impl Song as Comparable { fn Less(self, rhs: Self) - bool { ... } }2.2 函数重载被限制在同一库内唯一机制意味着 Carbon 中的函数重载被严格限制为只有同一库内一起定义的签名才能参与重载。跨库/跨包为某个名字新增重载是不允许的。这就杜绝了 C 那种往重载集里随手加一个函数的意外扩展。2.3 与 C 互操作operator 与 swap 必须对应到 Carbon 接口由于开放扩展只能通过接口进行与 C 互操作时运算符和swap在 Carbon 侧都需要有对应的接口。这一设计决策在当前仓库的 prelude 源码中已经落地运算符全部以接口形式定义在 core/prelude/operators.carbon聚合导出与 core/prelude/operators/arithmetic.carbon 等文件中。例如// Addition: a b. interface AddWith(Other: type) { let Result: type; fn Op(self, other: Other) - Result; } // Addition with assignment: a b. interface AddAssignWith(Other: type) { fn Op(ref self, other: Other); }该目录下还按运算类别拆分为as.carbon、bitwise.carbon、comparison.carbon、index.carbon、deref.carbon可见在 Carbon 里为类型定义运算符就是实现对应接口——例如实现AddWith即为类型定义。这印证了 generics 总览中的表述To overload an operator, implement the corresponding interface from the standard library.三、选择 interfaces 而非开放重载的理由原则文档列出了 interfaces 作为开放扩展机制相比开放重载的若干优势允许泛型被独立类型检查这是最主要的优势。接口固定了调用签名泛型函数体可以在不知道具体类型参数的情况下完成语义检查definition checking这也是实现独立编译的前提docs/design/generics/terminology.md#definition-checking。上下文敏感性更低符合 低上下文敏感原则。开放重载的名字解析取决于导入了什么是典型的上下文敏感行为接口实现则是显式声明不随导入集合漂移。泛型具有 coherence一致性/相干性Carbon 的泛型是 coherent 的——某个类型是否实现了某接口、实现是什么有唯一答案不依赖上下文如当前文件导入了哪些库。而开放函数重载会因导入内容不同而解析出不同名字。术语表中进一步说明这种一致性通过孤儿规则orphan rule限制实现可声明的位置如非参数化的实现必须与接口或类型同库与重叠规则overlap rule多个实现适用时选最特化者共同保证。简化 Carbon 导出到 C 的内容闭式重载closed overloading让从 Carbon 导出什么到 C更加简单可控。实现表达显式意图接口实现是显式的——我声明这个类型支持这个操作。与之相反向跨文件重载集添加一个函数可能是意外的、偶然的。四、接口的分组能力把契约当作整体接口还提供了开放重载天然缺乏的能力把一组函数组织在一起并表达这组函数必须全部被实现这一约束。原则文档用随机访问迭代器举例一个随机访问迭代器有多个方法。如果 C 模板函数只是碰巧访问了某个类型恰好定义的部分方法代码当下可以工作但一旦代码被修改为访问另一子集就会在更晚的时候失败。接口把整个方法集合作为契约呈现类型要么完整实现、要么不实现避免了部分匹配带来的延迟失效问题。这与interface可以require其他接口、extend其他接口的能力相辅相成见 docs/design/generics/overview.md 中Iterable要求Equatable的示例使接口能够表达分层的能力约束。五、备选方案为什么不用特殊方法名原则文档也对比了第二种主流方案——特殊方法名special method namesC 用operator关键字如operatorPython 用首尾双下划线的方法名如__add__、__eq__。接口在实现可定义在哪里这一点上更灵活。举例来说对Vector(T)类如果用特殊方法名只能定义在Vector(T)的类定义内部而用接口Vector(MyType)上的还可以在MyType一侧实现。C 通过允许非成员运算符重载获得了同样的灵活性但代价是要从可能非常大的开放重载集中挑选最佳匹配——这又回到了开放重载的问题上。换句话说接口方案兼顾了两者的优点——既有特殊方法名方案实现位置灵活和意图显式的特质又避开了开放重载的巨型重载集匹配成本。六、原则在 Carbon 目标体系中的位置作为项目 原则文档集 中的一员该目录下的原则用于澄清但不取代语言目标本原则直接服务的是 代码易读、易理解、易编写 这一语言目标显式的接口实现让读者无需追踪跨文件重载集即可理解代码行为低上下文敏感与 coherence 也让代码的解析结果可预测。提案本身也给出了其依据该提案在追求上述易读易写目标。早期论据在提案的 Alternatives considered 一节中被引用为外部文档本文不再展开。七、总结一条原则如何贯穿语法、泛型与互操作单一静态开放扩展机制看似只是一句话但它贯穿了 Carbon 语言的多处设计设计面该原则的体现运算符重载通过实现标准库接口如AddWith、Negate完成见 core/prelude/operators/泛型检查接口签名使泛型定义可独立类型检查definition checking名称解析闭式重载 coherence名字解析不随导入变化C 互操作运算符、swap等 C 扩展点需在 Carbon 侧映射为接口代码理解显式接口实现表达意图杜绝意外加入重载集理解这一原则是读懂 Carbon 泛型设计docs/design/generics/overview.md、术语表以及后续接口即扩展点系列设计如参数化接口、孤儿规则的钥匙——它解释了为什么 Carbon 选择用接口而不是重载来表达一切可扩展能力。【免费下载链接】carbon-langCarbon Languages 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),仅供参考
返回列表