ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Mojo 模式匹配(Pattern Matching)设计提案深度解析:从 match/case 到 if-let 的统一模式体系

Mojo 模式匹配(Pattern Matching)设计提案深度解析:从 match/case 到 if-let 的统一模式体系 Mojo 模式匹配Pattern Matching设计提案深度解析从 match/case 到 if-let 的统一模式体系【免费下载链接】mojoThe Modular Platform (includes MAX Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo导读本文基于仓库中 pattern-matching.md 概念提案Concept proposal展开系统梳理 Mojo 语言在模式匹配方向上的设计蓝图它如何把模式Pattern提升为语言语法与语义模型中的一等公民如何复用 Mojo 已有的解构destructuring基础设施以及如何以match/case、条件匹配if pattern expr、守卫guard等形式落地。通过阅读本文你将掌握模式匹配的核心概念可反驳模式、穷尽性、递归组合、提案规划的 7 类模式形态与未来方向并能结合仓库中 解析器测试 等源码证据理解 Mojo 现有的__match实验性实现与提案目标之间的对应关系。阅读前提本文涉及的模式匹配语法目前以提案形态存在于 Mojo/proposals 目录仓库中的解析器测试使用的是__match实验语法见后文仓库中的落地证据一节。两者在概念上一脉相承但需注意区分设计目标语法与当前实验语法。一、提案背景为什么 Mojo 需要模式匹配模式匹配Pattern matching是一种通用机制测试一个值是否具有特定结构若是则将该值的组成部分抽取提取到新的绑定中。Python 已经具备丰富的模式匹配能力Mojo 的目标是拥抱 Python 已有的核心语法并根据自身需求尤其是所有权处理做针对性扩展。提案给出了一个最直观的示例——同时测试元组的一部分、绑定另一部分def inspect(point: Tuple[Int, Int]): match point: case 0, 0: print(origin) case x, 0: print(on the x axis:, x) case 0, y: print(on the y axis:, y) case x, y: print(point:, x, y)这里的本质操作是**结构化structural**的将值的组成部分与模式逐一比对同时把其余部分绑定到名字上。提案强调模式的用途远不止match语句。Rust 与 Swift 都把模式当作可跨声明与控制流结构复用的通用语言概念Rust 在普通解构中使用模式let (x, y) point; if let [first, second] values.as_slice() { use_values(first, second); } let Point { x, y } point;Swift 在switch、if case、guard case、for case等结构中使用模式if case let (x, 0) point { print(x axis:, x) }据此提案为 Mojo 提出三点分解原则模式描述值匹配、分解与绑定语义独立于任何特定控制流结构模式递归组合绑定、通配符、元组、序列、值、结构体以及未来的模式形态应纳入统一模型而非互不相干的语言特性不同语言结构可不同方式消费模式match语句可依次尝试多个模式if可测试单个模式普通解构则可能要求结构确定。因此核心目标是把模式确立为语言语法与语义模型的一等公民而不是把match设计成孤立的控制流特性。这也天然给出了实现路径语言可先为现有值形态定义模式的语法、绑定行为、可反驳性refutability、控制流语义与降级lowering模型其他语言特性包括独立的枚举提案随后只需新增模式形态无需自建解构机制。二、Mojo 现有的解构能力Destructuring in Mojo Today模式匹配并非凭空而来。Mojo 的声明与赋值中已经支持丰富的解构destructuring提案列举了现状# 简单赋值 var x get_value() # 元组解构 var (x, y) get_pair() # 广义赋值目标 (var x, ref y, _) get_values() (a[i], b[j]) get_pair() # 这些可以任意嵌套 (var x, (ref y, _)) get_nested_values()关键观察在于Mojo 已经拥有一套可组合的语言来描述值的组成部分如何分配到绑定与赋值目标中。var x、ref y、_、广义 lvalue 以及(p1, p2, ...)都能递归地参与解构。与 Python 类似在语法无歧义的上下文中元组解构甚至不需要圆括号。Mojo 还在其他类赋值上下文中支持解构例如for循环目标注意for循环目标默认以imm绑定隐式绑定名字for key, value in entries: ...当前解构的一个关键约束是必须在编译期静态已知会成功。例如给定(var x, var y) value编译器必须知道value具有合适的二元元组结构结构不兼容的值是编译期错误而不是运行时失败。 NOTE提案原注当前元组解包支持极度硬编码于Tuple类型将来应当泛化。三、解构目标 vs. 匹配模式两种不同的语法语境解构与模式匹配密切相关但并不等同。本提案遵循 Python 的整体设计将二者保持为不同的语法语境解构目标destructuring target描述值的组成部分去向何处匹配模式match pattern询问值是否满足某条件并在满足时引入绑定。match get_pair(): # 绑定 x并检查是否为 0。 case x, 0: use(x) # 错误任意表达式/lvalue 不是值模式。 case var x, a[2]:匹配模式刻意使用受限语法而不是把任意 Mojo 表达式当作相等测试。模式可以包含字面量和其他显式支持的形态但不接受任意调用、运算符、下标或广义 lvalue——这避免了与产生 lvalue/引用的表达式发生意外交互并保持模式可静态理解。普通赋值则继续使用 Mojo 现有的赋值目标规则(var a, 0) get_pair() # 错误0 不是赋值目标。 0 get_value() # 错误。 a[2] get_value() # 赋值进广义 lvalue 是允许的。尽管存在差异解构目标与匹配模式共享大量递归结构元组分解、var/ref绑定、通配符、序列分解等形态可复用同一套底层编译器机制——这一点在提案末尾的实施路线中会被反复强调。四、match语句语义与可反驳性match语句是使用匹配模式最直接的方式。提案以按长度分类动态列表为例引入[a, b, c]序列模式match values: case []: print(empty) case [x]: print(one element:, x) case [x, y]: print(two elements:, x, y) case _: print(many elements)概念上match只求值一次主题subject然后按源码顺序逐个尝试 case模式匹配成功 → 其引入的绑定在 case 体内可用并执行该体模式匹配失败 → 继续下一个 case。由此引出模式匹配最重要的一对概念不可反驳模式irrefutable与可反驳模式refutable。不可反驳模式在静态上保证匹配输入类型的每一个值case x: case var y: # 拷贝进可变局部变量 case _:可反驳模式是否匹配取决于运行时值。定长序列模式是简单例子case [x, y]:若主题是动态大小的列表仅当其恰好包含两个元素时匹配成功。值模式value pattern是另一类重要的可反驳形态match point: case 0, 0: print(origin) case x, 0: print(on the x axis:, x)这里case x, 0先分解元组、把第一个元素绑定到x并要求第二个元素匹配0任一要求失败即转向下一个 case。与 Python 一致Mojo 只支持常量值模式——常量整数、浮点数、布尔、字符串等。模式递归组合可反驳性也随之递归组合match values: case [(x, 0), a_pair]: handle_special_value(x, a_pair) case [x]: handle_one_value(x) case _: handle_other_lengths(values)因此定义收敛为当编译器能证明模式匹配输入类型的每个值时模式是不可反驳的否则是可反驳的。可以证明永远不匹配的模式应当在编译期被诊断。例如用三元元组模式匹配静态已知的二元元组不是运行时匹配失败——它本身就是非法代码 永远无法匹配的模式是编译期错误。我们期望case var (a, b):在匹配值为 3 元元组时成为编译期错误。另外提案指出未在本提案中详述当匹配主题是参数表达式时Mojo 还应支持comptime match语句。穷尽性Exhaustiveness一个悬而未决的问题match语句是否必须覆盖每一种可能的输入值——要么编译器可证明 case 穷尽要么存在_这类最终的不可反驳模式match values: case []: ... case [x]: ... case _: ...穷尽性对某些模式形态是直截了当的但在完全一般化时难以判定。Mojo 初期可采用保守分析当编译器无法证明 case 覆盖所有可能值时要求存在最终的不可反驳 case另一种选择是不强制穷尽而是在不可反驳模式之后对不可达 case 给出警告。更精细的穷尽性与冗余分析可以随着模式语言日益丰富而逐步叠加。重点在于match本身保持简单——求值主题一次、按序尝试模式、执行第一个匹配成功的 case 体。丰富性来自递归组合、可独立演进的模式语言。五、待新增的模式形态Pattern Forms to AddMojo 已具备可直接迁移到匹配模式中的构件var/ref绑定、_通配符、元组分解与递归嵌套。提案建议在此基础上、遵循 Python 先例扩展以下形态1. 值模式Value Patternscase 0: case hello: case 0, 1: # 元组模式中的两个值与 Python 一致任意表达式不被支持也不应纳入首版实现。值得注意的是case Int:会绑定一个名为Int的新值而不是测试类型Int——该问题的解决方案见未来方向。2. 序列模式Sequence Patternscase []: case [x]: case [x, y]:当运行时序列长度可能不同时这些模式是可反驳的。同一套底层分解支持也可被普通解构赋值复用并应接入集合所遵循的某个trait。3. 变长序列模式Variable-length Sequence Patterns捕获前缀、后缀或剩余部分只允许一个*模式同样接入同一 traitcase [first, *rest]: case [first, *middle, last]:4. 或模式OR Patterns任一备选匹配即整体匹配case 0 | 1: case [var x, 0] | [var x, 1]:引入绑定的备选必须保证每条路径上绑定兼容。5. as 模式AS Patterns在递归应用一个模式的同时保留完整匹配值产生一个imm绑定case [var x, var y] as value: ...6. 结构体模式Struct Patterns分解结构体类值case Point(xx_value, yy_value): print(x_value, y_value)7. 映射模式Mapping Patterns匹配特定键并分解其值case {name: name, age: age}: ...其他语言特性可继续追加模式形态。特别是独立的枚举/EnumLike提案见 enums.md将通过同一底层机制为和类型sum type备选的选择与分解新增模式。匹配守卫Match Guards最后匹配守卫很有用但它本身不是模式case [x, y] if x y: ...模式先匹配并引入绑定随后才求值守卫以决定该 case 是否被选中。六、条件模式绑定Mojo 的 if let模式匹配还天然适配 Mojo 现有的if语句无需引入独立的if let构造。一个有用的前提Mojo 中本来就是语句而不是表达式。因此可以把if条件的语法扩展为要么是布尔表达式要么是pattern expression的条件匹配子句if expression: ... if pattern expression: ...第二种形式中左侧使用完整的匹配模式语法解析而非普通赋值所用的受限解构语法。例如if var [a, 0] get_list(): use(a)右侧只求值一次并与[var a, 0]匹配仅当列表恰好包含两个元素、且第二个元素匹配0时条件成立成立则绑定a并执行体否则条件为假。这有意比普通解构更具表达力# 普通解构合法但长度错误时抛出异常。 var [a, b] get_list() # 普通解构非法因为 0 不是赋值目标。 var [a, 0] get_list() # 条件匹配合法失败走 false 分支。 if var [a, 0] get_list(): use(a) else: handle_other_shape()条件匹配成功引入的绑定限定在对应if体内if var [first, second] get_list(): use(first, second) # first 和 second 在这里不可用。这提供了 Rust、Swift 中通常写作if let的能力却无需引入新的let专属构造。if本身建立匹配模式上下文因此嵌套与可反驳模式自然组合if (var x, [var y, 0]) get_value(): use(x, y)不可反驳模式技术上也可出现在该位置if var x get_value(): use(x)但这样的条件静态已知必然成功很可能是错误。编译器应就此类情况给出警告正如它诊断其他已知为常量的条件一样。于是if条件有两种自然形态布尔表达式求值为True时条件成立条件模式匹配模式匹配时条件成立。if中的模式失败因此是普通控制流而非异常。同样的处理应适用于while循环条件while var [item, 0] get_next(): use(item)这为 Mojo 带来if let/while let的易用性同时复用了普通的if/while语法与case使用的同一套匹配模式语言。七、实施路线无需一次性大爆炸式实现虽然本提案描绘的模式匹配体系相当广泛但并不需要作为一个大型单体项目一次性实现。大多数部件彼此正交可以作为相对较小的子项目独立落地。核心投资可失败模式的递归表示关键实现投入是一个可能成功或失败的匹配模式递归表示以及一个降级模型将该模式应用于一个值后要么产出绑定要么报告失败。一个非常小的起点是字面量值模式与 Mojo 现有元组分解的组合match get_tuple_pair(): case (var x, 0): use(x) case _: ...Mojo 已经理解元组分解与var x新操作只是以语义测试第二个元素是否为0。这正好锻炼核心匹配机制表示可反驳模式只求值一次被匹配的值递归组合绑定模式与值模式仅在完整模式成功时产出绑定失败时把控制转移到下一个case。相关联项目可失败的序列解构var [a, b, c] get_list()这属于普通解构而非完整匹配模式语法但可共享同一套底层分解基础设施。序列支持不应硬编码到List应当定义一个序列类类型遵循的 trait暴露检查运行时形状与以恰当所有权语义投影元素所需的操作。同一协议随后可支撑序列匹配模式match values: case var [a, b, 0]: ... case _: ...以及后来的变长形态case var [first, *rest]: case var [first, *middle, last]:可独立分解的其他部件if/while中的条件匹配把模式失败解释为假条件或模式组合若干现有模式as 模式递归应用另一模式的同时保留完整匹配值结构体模式为分解结构体类值提供可扩展机制映射模式引入另一个可独立实现的结构协议匹配守卫在模式成功匹配后追加布尔过滤枚举/EnumLike提案enums.md利用同一基础设施新增一族模式穷尽性与冗余分析随更多模式形态落地而渐进精细化。架构要点这些特性应通过公共的模式与分解基础设施组合而不需要一次协同实现。八、未来方向任意值匹配Arbitrary Value Matching模式中的裸标识符引入绑定与 Python 一致也遵循 Mojo 中for语句的先例case (x, y): # 声明新的 imm 绑定 x 和 y那么裸名就不能再同时表示匹配现有同名值。这正是 Python 面对的歧义case x是捕获模式而case HttpStatus.OK这类限定名被解释为值模式——Python 刻意把点号名保留给这一用途。Mojo 应采纳同样的限定值约定case Color.red: case .blue: # 同一语法的推断形式 case SomeModule.special_value:但这并不能解决所有情况。特别是类型在 Mojo 中本身就是 comptime 值对类型做匹配会很有用def storage_kind[T: AnyType] - String: comptime match T: case Int: return integer case String: return string case _: return other语义上这不需要专门的类型匹配特性类型是值且其上定义了相等性。问题纯粹是语法性的——若裸标识符隐式绑定case Int就无法同时表示与现有值Int比较。Python 曾广泛研究此问题PEP 635 记录了^CONSTANT、$CONSTANT、CONSTANT等显式常量标记提案最终将其留作可能的未来扩展PEP 642 更进一步提出显式相等模式语法case expected:其显式含义是当主题等于该值时匹配。PEP 642 最终被否决Python 选择了更简单的初始设计但这一思想对 Mojo 很有意思。例如 Mojo 最终可以支持comptime match T: case Int: return integer case String: return string case _: return other以及更一般地case (x, expected): ...这给出干净的语法分工case x: # 隐式绑定 x。 case 42: # 字面量值模式。 case Color.red: # 限定值模式。 case expected: # 显式值模式。显式标记也为未来考虑更一般的表达式留出了位置case compute_expected(): ...首版无需支持任意表达式。在 Mojo 中表达式可能具有有趣的引用与 lvalue 行为因此把第一版约束为简单值引用可能更可取。关键是显式相等模式为广义值匹配提供了自然的扩展点——既优化了绑定的常用语法又保留了匹配任意现有值的无歧义手段还避免为类型引入特殊语义 Int只是一个值恰好是类型的值模式。九、仓库中的落地证据__match实验实现与解析器测试作为概念提案pattern-matching.md 描述的是设计目标语法match/case。而仓库源码中已经存在与之对应的实验性__match实现可作为验证提案语义的落地证据注意语法前缀差异。解析器实现从源码结构看__match的关键字与解析逻辑位于 Lexer.cpp词法识别与 ParserStmts.cpp语句解析这说明该语法已进入 Mojo 解析器的实现层面。解析器测试绑定语义statements/match.mojo 是一份带 FileCheck 断言的解析器测试覆盖了提案中的大量语义。例如其中验证了绑定类别与提案第二章、第五章的var/ref/imm 语义完全对应裸绑定是imm绑定寄存器值变为bound内存值变为不可变refmuttoimmvar绑定对主题做拷贝lit.ref.store__init__(copy:::)可被修改ref绑定存储指向主题的可变引用、不拷贝。测试还覆盖了值模式与相等测试降级case 0/case 1被降级为__eq____mlir_bool__hlcf.elif链对应提案第四章按序尝试 case的语义元组主题模式case (0, 0)通过__getitem_param__投影元素再__eq__多个元素条件用hlcf.elif求 AND对应提案第一章的inspect示例守卫case _ if c ! 0先做匹配、再在hlcf.elif内求守卫对应提案第五章匹配守卫可反驳/不可反驳case var x:始终匹配irrefutablecase 0:需要相等测试refutableas 模式case _ as s:绑定 ref 而非拷贝且寄存器可传register-passable主题不能用ref改用bind——这正是提案强调的所有权处理在实现层的体现或模式case 0 | 1降级为多个相等测试的 OR 组合枚举风格限定值模式case Color.red:与推断形式case .green:都能匹配——与提案第八章的Color.red/.blue设计一致。诊断测试错误处理语义statements/match_diags.mojo 验证了__match的诊断行为与提案的静态约束相印证__match语句必须至少包含一个case块否则报错case块内的绑定如var x不可泄漏到后续 case对应提案第六章if绑定作用域、以及case 失败即转向下一 case的隔离语义__match必须位于函数内部主题未知会报错且解析器能从错误中恢复、不会留下悬空的case。与枚举提案的关系enums.md 明确构建在本模式匹配提案之上枚举sum type的 case 选择与载荷分解将通过同一底层模式机制实现。测试文件中的struct Colorcomptime red Color()常量成员 __eq__即为无专门枚举语法时用模式匹配枚举风格值的可行路径演示。实现状态说明以上测试与解析器代码证明__match已进入 Mojo 编译器的实验实现阶段而提案正文的match/case公开语法、if pattern expr条件匹配、序列/映射模式等仍是设计蓝图具体可用性以官方发布为准。十、总结一个可渐进落地的统一模式体系本提案的核心洞见可以浓缩为三点模式是一等公民匹配、分解、绑定语义独立于控制流构造可被match、if、while、解构赋值等不同结构以不同方式消费递归组合优于孤岛特性绑定、通配符、元组/序列/值/结构体/映射等形态共享统一语法与降级基础设施新形态如枚举模式只需增量接入实施可按小项目推进从字面量值模式 元组分解的最小内核出发逐步叠加守卫、或模式、as 模式、条件匹配与穷尽性分析无需一次性大型工程。对于想要深入验证的读者建议按以下路径在仓库中继续探索设计总纲Mojo/proposals/pattern-matching.md姊妹提案枚举模式Mojo/proposals/enums.md解析器实现Mojo/lib/MojoParser/ParserStmts.cpp、Mojo/lib/MojoParser/Lexer.cpp语义测试Mojo/test/mojo-parser/statements/match.mojo诊断测试Mojo/test/mojo-parser/statements/match_diags.mojo标准库中case/comptime等既有模式的真实使用可从 Mojo/stdlib/std/builtin/simd.mojo 等文件检索match/case关键字观察现状【免费下载链接】mojoThe Modular Platform (includes MAX Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表