使用 CGP 在 Rust 中编程可扩展数据类型 - 第 2 部分:模块化解释器和可扩展访问者 | 上下文泛型编程

这是关于在 Rust 中使用 CGP 编程可扩展数据类型的系列文章的第二部分,主要内容如下:

  • 系列概述:包括模块化应用构建和可扩展构建器(第一部分)、模块化解释器和可扩展访问者(本部分)、实现可扩展记录(第三部分)、实现可扩展变体(第四部分)。
  • 扩展访问者模式:Rust 中的访问者模式常用于遍历抽象语法树或序列化有效负载,传统访问者模式存在固有缺陷,如扩展性受限,而 CGP 的可扩展访问者模式旨在解决这些问题,保持类型安全和可扩展性。
  • 表达式问题:以实现玩具算术语言的解释器为例,说明传统表达式定义方式存在的问题,如随着语言的发展,修改表达式枚举会导致所有相关函数的模式匹配代码都需要更新,导致紧密耦合,CGP 的可扩展访问者模式可解决此问题。
  • 评估计算机:通过定义独立的Plus结构体和Computer特质,实现了可扩展且解耦的评估逻辑,不同的算术运算(如加法、乘法、常量)都有各自的提供者,通过递归调用计算子表达式,最终得到结果。
  • 评估具体表达式:定义具体的表达式类型MathExpr,并通过#[cgp_context]声明定义解释器上下文Interpreter,使用delegate_components!宏进行组件调度,实现了从具体表达式到数值结果的评估。
  • 调度评估:通过DispatchEval提供者和MatchWithValueHandlers访问器调度器,实现了对MathExpr枚举的评估,避免了在 Rust 特质解析系统中的循环依赖问题,提供了干净、声明式且可扩展的方式来评估复杂的枚举。
  • 转换为 Lisp 表达式:将算术表达式转换为 Lisp 表达式,介绍了LispExpr枚举的定义和结构,通过ComputerRef特质和MatchWithValueHandlersRef访问器调度器,实现了从MathExprLispExpr的转换,展示了 CGP 的模块化和可扩展性。
  • 构建带有子枚举的变体:定义LispSubExpr枚举,用于表示 Lisp 表达式的子集,通过CanUpcast特质实现从LispSubExprLispExpr的安全类型转换,展示了 CGP 的向上转换机制在构建枚举值时的灵活性。
  • 高级技术

    • 二进制运算符提供程序:通过提取共享结构,实现了通用的BinaryOpToLisp提供者,用于处理加法和乘法表达式,减少了代码重复,提高了代码的可维护性和可扩展性。
    • 基于代码的调度:利用Code参数在ComputerRef特质中构建类型级 DSL,实现了基于计算意图(评估或转换)的代码调度,通过delegate_components!宏实现了灵活的组件组合,展示了 CGP 的分层调度模型的优势。
  • 扩展MathExpr:通过定义新的MathPlusExpr表达式类型,展示了 CGP 在支持语言扩展方面的模块化和灵活性,包括添加减法和取反操作,无需修改现有代码,提供者可以无缝工作。
  • 结论:可扩展变体和 CGP 访问者模式在模块化解释器设计中开辟了新领域,能够以可重用、解耦的组件进行表达式评估和转换,保持类型安全,无需在可扩展性和类型安全之间进行权衡。第三部分将深入探讨可扩展记录的底层实现细节。

此外,作者还提到可在GitHub 仓库找到示例的完整源代码,并且作者可被雇佣

阅读 11
0 条评论