SuperAPI AI 工具集

motoko-dot-notation-migration

编程与开发

查看项目

更新于 10-16 05:59

技能介绍

用途

把 Motoko 函数调用从旧的 Module.func(self, ...) 写法转换为新的 self.func(...) 点语法,针对 core 包中所有第一个参数命名为 self 的函数。该技能源自 motoko-bitcoin 项目的一次成功迁移,包含 core 2.2.0 的完整函数目录、一个经过实战检验的 Python 转换脚本,以及必须处理的全部关键陷阱。

适用场景:把 Motoko 项目从 base 迁移到 core,或升级 core 版本;用户要求把函数调用转换为点语法;用户要求现代化 Motoko 调用语法。

背景

在 Motoko 的 core 包(base 的后继者)中,许多模块函数将其第一个参数声明为 self。这些函数支持点语法:不再写 Module.func(self, arg2, arg3),而可以写 self.func(arg2, arg3)。

只有第一个参数字面命名为 self 的函数才支持这一写法。像 Array.tabulate、Array.fromVarArray、Blob.fromArray 等工厂函数并没有把 self 作为第一个参数,因此绝不能转换。

转换规则

零额外参数函数:Module.func(expr) → expr.func();Module.func<T>(expr) → expr.func<T>()。

多参数函数:Module.func(expr, a, b) → expr.func(a, b);Module.func<T>(expr, a) → expr.func<T>(a)。

嵌套调用会自然形成链式:Array.flatten(Array.map<T, U>(xs, f)) → xs.map<T, U>(f).flatten();List.toArray(listExpr) → listExpr.toArray();Blob.toArray(Text.encodeUtf8(t)) → t.encodeUtf8().toArray()。

关键陷阱

  1. 运算符优先级——点号绑定比中缀运算符更紧。在 Motoko 中,. 的绑定比所有中缀运算符(>>、<<、&、|、+、-、*、/、%、#、==、!=、and、or)都更紧。例如 value >> 24.toNat() 会被解析为 value >> (24.toNat()),因此若 self 表达式包含任何顶层中缀运算符,需先加括号再追加 .func()。

  2. 类型参数中含逗号——需跟踪尖括号深度。按第一个逗号拆分 Module.func(self, rest...) 时,必须跟踪 <> 的深度。像 <Nat64, [Nat8]> 这类类型参数里的逗号不是参数分隔符。若忽略 <> 而在第一个逗号处拆分,会损坏代码。规则:在查找逗号时,像跟踪 depth_paren 一样跟踪 depth_angle,且仅当 depth_angle > 0 时才对 > 递减,以免把比较运算符误认为尖括号闭合。

  3. 不得转换的函数。以下函数第一个参数不是 self:Array 的 tabulate、fromVarArray、fromIter、empty、repeat;Blob 的 fromArray、fromVarArray、empty;Text 的 fromArray、fromIter、fromVarArray;List 的 empty、repeat、tabulate、fromIter、fromArray、fromVarArray;VarArray 的 empty、repeat、fromArray、fromIter、tabulate;Nat 的 range、min、max、rangeByInclusive;Nat8/16/32/64 的 fromNat、fromIntWrap、fromNat16、fromNat32、fromNat64(构造器);Int 的 abs、fromNat、range、min、max;Char 的 fromNat32、fromText;Runtime 的 trap。

不过 Array.fromVarArray(x) 有点语法等价写法 x.toArray()(经由 VarArray.toArray)。这不由点语法转换脚本处理,属于另一项独立重构。

  1. 点语法要求该类型的模块仍被导入。在值上用点语法调用方法(例如 someBlob.toArray())时,即便调用代码中未显式出现模块名,该值类型对应的模块(如 Blob)仍必须导入。常见例子:Blob(用于对 Blob 返回值调用 .toArray()、.size());Array(用于对 [T] 值调用 .flatten()、.foldLeft()、.map()、.sliceToArray());Nat(用于对 .size() 得到的 Nat 值调用 .toText());VarArray(用于对 [var T] 值调用 .toArray())。删除这些导入会编译失败,报错 field X does not exist in type 并提示导入模块。

  2. 必须跳过字符串字面量。扫描运算符或逗号时,要跳过字符串字面量(包括转义引号),避免字符串内部的运算符触发误判。

  3. 数值字面量无需括号。42.toText() 和 0xFF.toNat() 在 Motoko 中合法——数值字面量可直接使用点语法,不需要括号。

函数目录(core 2.2.0)

零额外参数函数(仅 self):这些只接收一个名为 self 的参数,转换 Module.func(expr) → expr.func()。包含 Nat8/Nat16/Nat32/Nat64 的各类 toNat/toText/toNatX/toChar;Nat 的 toText/toInt/toFloat/toNatX;Int 的 toText/toNat/toFloat/toIntX;Char 的 toNat32/toText/isDigit/isAlphabetic/isWhitespace/isLowercase/isUppercase;Blob 的 toArray/toVarArray/isEmpty/size/hash;Text 的 toArray/toIter/toVarArray/encodeUtf8/decodeUtf8/size/isEmpty/reverse/toText;Array 的 toVarArray/size/isEmpty/keys/values/reverse/enumerate;VarArray 的 toArray/clone/size/isEmpty/keys/values/reverse/enumerate;List 的 size/toArray/toVarArray/isEmpty/first/last/clone/clear/values/keys/enumerate/reverse/removeLast。

多额外参数函数(self 加更多参数):转换 Module.func(expr, a, b) → expr.func(a, b)。注意其中一些也可只用 self 调用(例如 Array.flatten(xs) 无额外参数),多参数处理逻辑必须兼顾两种情况。列表涵盖 Array 的 flatten/map/filter/filterMap/flatMap/foldLeft/foldRight/concat/sliceToArray/forEach/mapEntries/sort/find/findIndex/equal/compare/all/any/indexOf/contains/toText/binarySearch/isSorted/mapResult/range;VarArray 的 sortInPlace/map/filter/filterMap/flatMap/foldLeft/foldRight/concat/forEach/sort/find/findIndex/equal/compare/all/any/indexOf/contains/toText/mapResult;Text 的 replace/tokens/contains/split/startsWith/endsWith/concat/map/flatMap/foldLeft/join/trimStart/trimEnd/trim/stripStart/stripEnd;List 的 add/at/get/put/map/filter/forEach/sort/find/findIndex;Blob 的 compare/equal。

如何为新 core 版本更新目录

当 core 包更新时,可能新增函数或修改签名。技能提供了一段 shell 脚本,可从实际源码重新生成目录:扫描 CORE_SRC/*.mo,匹配 public func 或 public let 声明,判断第一个参数是否为 self,分别列出“有 self”和“无 self”的函数。注意部分 public let 使用内联 lambda 语法(如 public let toNat = Prim.nat8ToNat; 或 public let encodeUtf8 : Text -> Blob = Prim.encodeUtf8; 此时 self 是隐式的)。对引用 Prim 单值函数的 public let 绑定,若类型签名是 T -> U,则那个单参数就是 self,支持点语法。扫描后将函数分类为 ZERO_EXTRA、MULTI_EXTRA、NO_SELF,并更新转换脚本中的对应列表,且仅保留项目中实际用到的函数,以避免误匹配同名的本地函数。

转换脚本

技能提供了 convert_dot_notation.py,使用步骤为:按自己的 core 版本和项目更新 ZERO_EXTRA 与 MULTI_EXTRA 列表、设置 src_dir 为项目源码目录、运行 python3 convert_dot_notation.py、运行 npx mops test 测试、若全部通过则删除该脚本。脚本引擎包含 needs_parens 等辅助逻辑,用于判断表达式是否需要加括号,并逐层跳过括号、方括号、花括号和字符串字面量后扫描顶层中缀运算符。

适用场景

适用于 Motoko 项目从 base 迁移到 core 或升级 core 版本的现代化改造,尤其是大量使用 core 模块函数调用、需要批量改写为点语法的代码库。技能提供了成体系的规则、陷阱说明和自动化脚本,便于在保证语义正确的前提下批量迁移,并借助测试验证结果。

限制

技能内容聚焦于 Motoko 点语法迁移这一具体任务,资料中未提供其他通用能力说明。使用时应严格按转换规则和陷阱处理,并在运行脚本后通过测试验证;具体项目适配仍需自行调整配置。