Carbon 语言 sum types(和类型)设计演进解析:从 p002187 提案看模式匹配接口的现代化改造 Carbon 语言 sum types和类型设计演进解析从 p002187 提案看模式匹配接口的现代化改造【免费下载链接】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 语言仓库中的设计提案 proposals/p002187-update-sum-types-design.md 展开系统梳理这份提案对最早 p000157-design-direction-for-sum-types.md 确立的和类型sum types设计方向的五处关键修正——从Matchable接口参数到关联类型的演进、Match.Op命名落地、name: Type参数书写顺序的规范化以及生成式泛型语法语义的同步。读完本文你将掌握 Carbon 和类型模式匹配机制从早期草案到当前设计文档与工具链实现之间的完整演变脉络并能对照仓库中的设计文档、Prelude 源码与检查器测试用例验证每一项结论。提案背景为什么 sum types 设计需要更新p002187是一份典型的设计同步提案Update proposal。它并非从零设计和类型而是针对 2020 年的早期提案 p000157-design-direction-for-sum-types.md编号 #157进行修订原因是自 #157 以来语言的诸多方面已经发生了变化原设计中的某些细节与语言其余部分不一致或者在后续语言发展的背景下可以被表述得更精确。简而言之原提案当时使用的很多语法、命名和机制都是占位性质的——例如interface、impl、method等关键字在 #157 中明确标注为 placeholderType:$$ T这种类型在前、名字在后的参数写法、将 continuation 接口作为Matchable的接口参数、把模式匹配入口命名为Matchable.Match等都随着语言正式定型而显得过时。p002187 的作用就是把这些过时或不够精确的部分逐一对齐到语言现状。从仓库现状看这份修订的精神已经沉淀进当前设计文档 docs/design/sum_types.md该文档中的Match接口、Match.BaseContinuation、Match.Op等形态正是 p002187 所描述的目标形态。因此研读 p002187实际上就是理解当前 Carbon 和类型与模式匹配设计的关键钥匙。提案核心五项设计修正p002187 的正文非常精炼其全部实质内容浓缩为五点将 continuation 接口从Matchable的接口参数改为关联类型associated type对于既可以用Match又可以用匹配的 pattern要求两种实现都必须良构well-formedalternative 声明改用name: Type顺序取代原来的Type: name采用当前最新的泛型generics与类类型class types语法和语义将Matchable.Match更名为Match.Op以遵循 issue #1058 的决议。下面逐一展开并结合仓库源码佐证每一处变更的落点。变更一continuation 接口从接口参数变为关联类型这是整份提案中最核心的机制调整。在 #157 的原始设计中类型通过如下方式接入模式匹配impl Matchable(MatchContinuation) { method (Ptr(Self): this) MatchMatchContinuation:$ Continuation }即Matchable是一个带接口参数的接口参数MatchContinuation描述该类型的全部 alternatives再由类型的Match方法接收编译器生成的 continuation 实例并分发调用。而在 p002187 之后即当前 docs/design/sum_types.md 中的形态Match接口被重新定义为interface Match { interface BaseContinuation { let ReturnType: type; } let template Continuation: type; fn OpC: Continuation - C.(BaseContinuation.ReturnType); }可以看到三个关键结构变化Continuation成为Match的关联类型let template Continuation: type;不再作为接口参数出现在Matchable(...)的位置continuation 接口的约定被提炼为Match.BaseContinuation这个内嵌接口要求Continuation必须extend Match.BaseContinuation并提供ReturnType关联常量模式匹配的入口方法被命名为Op签名为fn OpC: Continuation参数是C*指针而非值传递。这一调整的价值在于它避免了接口上的重载解析这一从未规划的机制需求。若 continuation 是接口参数理论上一个类型可以通过实现多次Matchable(C)来支持多种模式匹配方式但这要求编译器在Match(C)的多个实现之间做类似重载决议的选择——见本文后续备选方案一节语言并没有为此提供机制。改为关联类型后一个类型只有一份Match实现语义清晰且与其余语言机制一致。仓库中 core/prelude/types/optional.carbon 的Optional类正是这一机制的样板实现class Optional(T: OptionalStorage) { fn Some(value: T) - Self; let None: Self; ... impl as Match { interface Continuation { extend Match.BaseContinuation; fn Some(ref self, value: T) - ReturnType; fn None(ref self) - ReturnType; } fn OpC: Continuation - C.ReturnType { if (self.has_value) { return continuation-Some(self.value); } else { return continuation-None(); } } } }这段源码是 p002187 所有结论的最终解释权Continuation以内嵌接口associated interface形式定义并extend Match.BaseContinuationOp接收C*且要求恰好调用一次 continuation 方法。同时注意docs/design/sum_types.md中还给出了编译器为match语句生成 continuation 实现类的示意形态__MatchStatementImpl说明匹配时编译器会把各case体打包进续延的方法体内再以my_opt.(Match.Op)({} as __MatchStatementImpl)的方式驱动分派。变更二Match与两种匹配路径都必须良构原始设计中存在一个未被严格约束的语义含糊点像case Result(Int, String).Value(0)这样的表达式形态的 pattern既可以解释为——把Value(0)当作普通函数调用求值再用与 scrutinee 比较也可以解释为——通过Matchable机制调用 continuation。p000157 当时选择把选择权留给编译器两种解释都允许。p002187 将这一条收紧为凡是既能用Match又能用匹配的 pattern两种实现都必须良构并由编译器强制执行。也就是说语言不再放任含糊而是要求类型的作者保证两种路径都能成立具体采用哪种路径生成代码仍是未指定的实现细节。这一点在 docs/design/sum_types.md 中得到了明确的继承与强化文档进一步给出了一个直接推论强烈建议用户自定义和类型为每个 alternative 提供同名同参的工厂函数或常量成员使得对模式匹配结果再求值能正确重现工厂函数参数。文档还专门举了None的例子说明后果case Optional(i32).None() ...是不良构的Optional(i32).None()具有 alternative pattern 的形态但以实现时Optional(i32).None()不是合法表达式若把None定义为工厂函数而非常量则结论恰好相反None()合法而None不合法。这条规则从设计上保证了构造语法与匹配语法的镜像对称——这正是 #157 中工厂函数与 continuation 方法互为逆运算思想的正式化。变更三alternative 声明统一为name: Type顺序这是语法层面的对齐。回顾 #157 中的原始写法choice Result(Type:$$ T, Type:$$ Error) { Success(T: value), Failure(Error: error), Cancelled }这里无论是类型参数Type:$$ T还是 alternative 参数T: value都采用类型在前、名字在后的旧式顺序同时:$$是早期表示模板参数的记号。p002187 明确要求 alternative 声明改用与当今语言一致的name: Type顺序。对照当前设计文档 docs/design/sum_types.md 中的写法新形态一目了然choice OptionalI32 { Some(value: i32), None } choice Optional(T: type) { Some(value: T), None }T: type表示模板/泛型参数type是类型的类型value: i32、value: T表示名为value、类型为i32/T的参数。参数名除文档作用外不参与语义value仅是文档性质的这与 #157 时代的设计精神一脉相承但书写顺序已完全现代化。变更四采用当前泛型与类类型语法语义#157 通篇使用的是 placeholder 语法Type:$$ T模板参数、method (Ptr(Self): this) Match[...]等并且当时的语言假设是接口可以带接口参数。p002187 要求整体迁移到语言当时的最新语法语义——即上文展示的T: type泛型参数、impl as Match的类内实现、fn Op[C: Continuation]的 generic 约束等。这一变更直接服务于变更一关联类型机制正是建立在接口可以有内嵌接口与关联类型这一泛型能力之上的。从源码结构可以推断这一现代化过程还与工具链同步推进仓库中的choice关键字解析与检查逻辑位于 toolchain/check/handle_choice.cpp其行为由 toolchain/check/testdata/choice/basic.carbon 与 toolchain/check/testdata/choice/params.carbon 等测试用例覆盖说明choice声明已从设计草案落进了检查器的实现与测试体系。变更五Matchable.Match更名为Match.Op这是最轻量的一处命名修订。早期设计中模式匹配入口叫Matchable.Match其中Matchable是接口、Match是其方法。p002187 将接口名简化为Match方法名改为Op理由是遵循 issue #1058 关于模式匹配运算符pattern matching operator命名的决议——用通用的Op后缀Carbon 中运算符实现的标准命名如Op也用于各类运算符接口取代容易与模式匹配概念混淆的Match。这一点同样在 docs/design/sum_types.md 与 core/prelude/types/optional.carbon 中完全落地接口叫Match方法叫Op调用形态为continuation-Some(...)/my_opt.(Match.Op)(...)。备选方案剖析为什么否决了这两条路线p002187 在正文末尾记录了在设计上述修订时考虑过的两个备选方案理解它们有助于把握修订的取舍逻辑。备选方案一保留 continuation 接口作为参数如果继续把 continuation 作为Match的接口参数理论上可以允许一个类型以多种方式支持模式匹配——只需为多个 continuation 接口分别实现Match(C)即可。但代价是编译器需要一种接口上的重载解析机制来从同一个和类型的多份Match(C)实现中选出与编译器构造的 continuation 最匹配的那一份。p002187 明确指出语言没有规划任何这样的重载机制我们没有足够有说服力的用例去推动引入它。因此在多态能力与语言复杂度之间提案选择了后者放弃多实现可能性换取机制的简单与确定。当前Match接口中Continuation作为单一关联类型的设计正是这一取舍的直接产物。备选方案二要求 continuation 执行整个case体另一种更激进的形态是让Match.Op把 alternative 参数解包到一个局部 lvalue传给 continuation并在 continuation 返回后再把可能被修改的值打包写回和类型对象。这样做的好处是支持对 alternative 参数的原地修改in-place mutation——例如对Optional(Foo)中存储的值调用可变方法甚至在底层参数并非该类型的 lvalue、必须由Match.Op解包的情况下也能工作。但 p002187 的判断是这相当投机fairly speculative它对当前支持的只读read-only匹配场景并不适用因此不必预先约束编译器的实现方向。换句话说这个问题被刻意留白避免在设计早期就为编译器背上一份解包-回调-回写的执行模型。这也解释了为什么当前 docs/design/sum_types.md 中只要求Match.Op在返回前恰好调用一次 continuation 方法而不规定case体代码的执行位置——文档甚至明确说明编译器生成的 continuation 方法体不要求包含case体的代码可以只存储参数值并返回一个标识case体的索引为编译器的代码生成保留了充分自由度。与这一开放问题相呼应的还有 #157 中遗留的 Open question用户自定义和类型乃至一般模式匹配如何支持允许你修改底层对象的 name binding可参见 proposals/p000157-design-direction-for-sum-types.md 的相关章节。设计全貌从 p002187 看当前 Carbon 和类型的完整图景将 p002187 的修订放回更大的设计语境中当前仓库所呈现的和类型体系由三个层次构成第一层choice声明语法糖层。用户用choice Optional(T: type) { Some(value: T), None }这种极简语法定义和类型编译器负责生成 discriminator、存储布局、工厂函数、静态常量以及Match实现。alternative 列表与函数声明同语法但省略fn与返回类型无参数 alternative 可省略括号如Nonedefault关键字可出现在列表中语义等同于 continuation 接口中的operator default——它要求所有针对该类型的match必须备好default分支从而保护类型作者未来新增 alternative 的权利避免破坏客户端构建。第二层用户自定义和类型底层机制层。class类型通过实现Match接口含BaseContinuation关联接口与Op方法获得模式匹配能力实现对存储表示、成员与方法的完全控制。core/prelude/types/optional.carbon 中的Optional就是这一层的现实范本——它通过OptionalStorage接口抽象出两套存储策略通用类型用boolMaybeUnformed(T)见其中DefaultOptionalStorage指针类型则直接用空指针表示None从而与 C 可空指针 ABI 兼容。这两套表示正是 #157 所设想的discriminator 打包进 padding/指针低位类自定义表示的具体化。第三层模式匹配语义match语句层。match编译期间编译器为 scrutinee 类型合成 continuation 实现类把各case体填入对应方法然后调用Match.Op完成分派。Match.Op必须恰好调用一次 continuation 方法而既可用Match又可用的表达式形态 pattern则受 p002187 的良构性约束保证两种路径都成立但实现细节留白。结语一份小而准的设计同步样本p002187 正文只有五十余行却精准完成了四项历史债的清偿接口参数到关联类型的架构调整、双路径匹配的良构性收口、name: Type语法对齐、以及Match.Op的命名落地。它没有引入新功能而是把 #157 时代占位语法 开放问题的设计方向校准到与语言现状一致的状态。读者若想深入验证本文结论推荐依次阅读更新提案原文proposals/p002187-update-sum-types-design.md被更新的原始提案proposals/p000157-design-direction-for-sum-types.md当前设计文档docs/design/sum_types.md语言中模式匹配的通用设计docs/design/pattern_matching.md标准库样板实现core/prelude/types/optional.carbon工具链实现与测试toolchain/check/handle_choice.cpp、toolchain/check/testdata/choice/basic.carbon对于希望参与 Carbon 设计讨论的读者这类Update proposal本身就是一种值得借鉴的演进方式当语言整体向前移动时回头校准早期设计决策而不是放任其与现状脱节——这正是 p002187 留给我们的方法论启示。【免费下载链接】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),仅供参考