motoko-core-code-improvements

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Purpose & Scope

目的与范围

Use this skill after test pass status to raise readability and consistency without changing behavior.
This skill focuses on mechanical, semantics‑preserving improvements:
  • Aggregate imports into sections (1) mo:core/... (2) other mo:*/... from mops or similar third‑party sources (3) local project modules; sort each section alphabetically per file
  • Prefer dot‑notation where available in
    mo:core
  • Clean up truly unused
    import
    lines while respecting implicit needs created by dot‑notation
  • Remove redundant
    return
    in single‑expression functions
  • Use direct string‑to‑Blob assignment for constant ASCII strings where appropriate
Safety first:
  • Run each improvement category independently; commit after each to isolate diffs
  • Prefer scripted, reviewable changes; use audit checks provided below
  • Rebuild after every category; run tests if present

在测试通过后使用本技能,可在不改变代码行为的前提下提升可读性与一致性。
本技能专注于语义保留的机械性改进:
  • 将导入内容归为三类:(1) mo:core/... (2) 来自mops或类似第三方源的其他mo:*/... (3) 本地项目模块;按文件对每个部分进行字母排序
  • mo:core
    中优先使用点表示法(dot-notation)
  • 清理真正未使用的
    import
    行,同时尊重点表示法带来的隐式依赖需求
  • 移除单表达式函数中冗余的
    return
    语句
  • 在合适的场景下,对常量ASCII字符串使用直接的字符串到Blob赋值
安全第一:
  • 独立执行每一类改进;每完成一类就提交代码,以隔离差异
  • 优先使用可脚本化、可审核的变更;使用下方提供的审计检查方法
  • 完成每一类改进后重新构建项目;若有测试则运行测试

AI Quick Checklist (Do Not Skip)

AI快速检查清单(请勿跳过)

  1. Preconditions
  • Project compiles on
    mo:core
    (see Migration Skill). Keep
    mo:base
    around only if still referenced; otherwise remove base dependency already.
  • Ensure consistent Motoko and dfx versions per migration skill (moc ≥ 1.3.0, dfx ≥ 0.31).
  1. Order of improvements (recommended)
  • A. Remove
    return
    in single‑expression functions
  • B. Convert to dot‑notation where available — see Motoko Dot‑Notation Migration Skill (
    skills/dot-notation-migration/SKILL.md
    )
  • C. Ensure necessary
    mo:core
    imports for dot‑notation — see Motoko Dot‑Notation Migration Skill (import mapping)
  • D. Clean up unused imports (be conservative re: dot‑notation)
  • E. Shorten local (sibling) import paths (drop the
    ./
    prefix where applicable)
  • F. Aggregate imports into three sections and sort each section alphabetically per file: (1)
    mo:core/...
    , (2) other
    mo:*/...
    from mops/third‑party, (3) local project modules
  • G. Use direct string‑to‑Blob assignment for constant ASCII strings where appropriate
  1. Verify after each step
  • Build all canisters or packages
  • Grep/audit with provided commands
  • Keep diffs minimal and readable
Acceptance Criteria
  • No compiler errors or warnings introduced by the changes (esp. missing imports)
  • No behavior changes; public interfaces unchanged unless stylistic
  • Imports are aggregated into three sections — (1)
    mo:core/...
    , (2) other
    mo:*/...
    from mops/third‑party, (3) local project modules — and each section is alphabetized; no truly unused
    import
    s remain
  • Dot‑notation is consistently used where directly supported
  • Constant Text strings are assigned directly to
    Blob
    without redundant
    Text.encodeUtf8
    calls

  1. 前置条件
  • 项目基于
    mo:core
    可编译(参考迁移技能)。仅当仍有引用时保留
    mo:base
    ;否则请移除base依赖。
  • 确保迁移技能中要求的Motoko和dfx版本一致(moc ≥ 1.3.0,dfx ≥ 0.31)。
  1. 推荐的改进顺序
  • A. 移除单表达式函数中的
    return
    语句
  • B. 在可用场景下转换为点表示法——参考Motoko点表示法迁移技能(
    skills/dot-notation-migration/SKILL.md
  • C. 确保点表示法所需的
    mo:core
    导入——参考Motoko点表示法迁移技能(导入映射)
  • D. 清理未使用的导入(对点表示法相关的导入需谨慎处理)
  • E. 缩短本地(同级)导入路径(在适用情况下移除
    ./
    前缀)
  • F. 将导入内容归为三类并按文件对每个部分进行字母排序:(1)
    mo:core/...
    ,(2) 来自mops/第三方的其他
    mo:*/...
    ,(3) 本地项目模块
  • G. 在合适的场景下,对常量ASCII字符串使用直接的字符串到Blob赋值
  1. 每一步完成后验证
  • 构建所有canister或包
  • 使用提供的命令进行Grep/审计
  • 保持差异最小且可读
验收标准
  • 变更未引入编译器错误或警告(尤其是缺失导入的情况)
  • 无行为变更;公共接口仅做风格调整,无功能变化
  • 导入内容被归为三类——(1)
    mo:core/...
    ,(2) 来自mops/第三方的其他
    mo:*/...
    ,(3) 本地项目模块——且每个部分按字母排序;无真正未使用的
    import
    语句残留
  • 在直接支持的场景下一致使用点表示法
  • 常量Text字符串直接赋值给
    Blob
    ,无需冗余的
    Text.encodeUtf8
    调用

A) Remove
return
in single‑expression functions

A) 移除单表达式函数中的
return

Pattern
motoko
// Before
func f(x : T) : U { return <expr>; };

// After
func f(x : T) : U { <expr> };
Notes
  • Only apply when the function body consists of a single
    return <expr>;
    statement.
  • Do not transform multi‑statement bodies or bodies that include
    try
    ,
    label
    ,
    switch
    , or
    await
    leading to different control flow.
  • A function with multiple
    return
    statements (e.g., early returns in
    switch
    cases like
    return null
    ) must NOT have any returns removed.
  • Single non-terminal
    return
    (early/conditional exit):
    if a function has exactly one
    return
    but it is not the final statement of the body (e.g., an early return inside an
    if
    or
    switch
    case, followed by a fall-through final expression), the function has two distinct return paths. Do NOT remove the early
    return
    . Instead, add an explicit
    return
    to the final expression as well, so the function has two
    return
    keywords total — one per exit path. This makes both control-flow exits explicit and consistent.
    motoko
    // Before — one explicit return, one implicit fall-through return
    func lookup(k : Key) : ?V {
      if (cache.contains(k)) { return cache.get(k) };
      table.find(k)
    };
    
    // After — both exit paths use `return`
    func lookup(k : Key) : ?V {
      if (cache.contains(k)) { return cache.get(k) };
      return table.find(k);
    };
  • return switch (...) { ... }
    at the end of a function is OK.
    Each case block ends with the expression being returned (no
    return
    keyword inside the cases). This is the preferred style when all cases produce values normally.
    motoko
    // OK — all cases produce values, no traps or throws
    return switch (decode(data)) {
      case (#ok key) { (key.x, key.y) };
      case (#err msg) { #err(msg) };
    };
  • Exception: if any case has a return-equivalent statement (
    Runtime.trap
    or
    throw
    ) that represents a genuine function exit, pull
    return
    inside the case blocks.
    Remove
    return
    before
    switch
    , then add
    return
    only in the case blocks that produce values. Cases with
    Runtime.trap
    or
    throw
    do not need
    return
    . This makes it clear which branches return and which abort:
    motoko
    // Avoid — trap is hidden inside return switch
    return switch (decode(data)) {
      case (#ok key) { (key.x, key.y) };
      case (#err msg) { Runtime.trap(msg) };
    };
    
    // Prefer — return only in value-producing cases
    switch (decode(data)) {
      case (#ok key) { return (key.x, key.y) };
      case (#err msg) { Runtime.trap(msg) };
    };
  • Unreachable traps are NOT return-equivalent. If
    Runtime.trap("unreachable")
    is used, or a comment or message indicates the branch is only reachable through a bug, that trap is not a normal function exit — it's a defensive assertion. In that case,
    return switch
    is fine and
    return
    should NOT be pulled inside:
    motoko
    // OK — the trap just guards an impossible case
    return switch (Jacobi.fromNat(x, y, 1, curve)) {
      case (null) Runtime.trap("unreachable");
      case (?point) point;
    };
Automation (example)
  • Grep candidates:
    grep -rn "func \\w\\+(.*) *:.*{ *return .*; *};" . --include="*.mo" | grep -v \.mops
  • Review matches; then apply with your editor or a scripted replacement.
Script Safety Requirements (learned from real migration)
  • A simple line-based return counter is NOT sufficient. You must track function boundaries using brace-depth parsing at the character level.
  • Nested functions: When a function body contains nested
    func
    declarations, skip the nested function's body entirely — only count returns at the direct (outermost) function scope.
  • Accurate function boundary detection: Use character-level scanning that handles string literals (skip
    "..."
    including
    \"
    escapes), comments (
    //
    line comments and
    /* ... */
    block comments), and tracks brace depth to find the true closing
    }
    of each function.
  • Counting rule: Count every
    return
    anywhere inside the outer function body, including inside nested control-flow blocks such as
    switch
    ,
    if
    ,
    for
    , and
    while
    , but excluding any
    return
    inside nested
    func
    bodies. A function is safe to rewrite only if this total count is exactly 1 and that single
    return
    is the terminal direct-body statement
    (only whitespace, comments, and an optional
    ;
    may follow it before the closing
    }
    ). If the single
    return
    is an early/conditional exit, the function has an additional implicit return path via its fall-through final expression — leave both alone (the script will not remove it; manually add an explicit
    return
    to the fall-through expression per the style note above).
Battle-tested Python script
Save as
remove_returns.py
in the project root, run with
python3 remove_returns.py
, then delete the script.
python
#!/usr/bin/env python3
"""
Remove terminal `return` from Motoko functions that have exactly one return
statement in their direct body (excluding nested functions).

Usage:
  1. Set src_dirs to match your project layout.
  2. Run: python3 remove_returns.py
  3. Run tests: npx mops test
  4. Delete this script after confirming all tests pass.

Safety:
  - Tracks function boundaries via character-level brace-depth parsing.
  - Skips string literals ("..." with \" escapes) and comments (// and /* */).
  - Detects nested `func` declarations and skips their bodies entirely.
  - Only removes the `return` keyword (+ trailing space) from functions with
    exactly 1 return at any depth within the direct body.
"""

import re
import glob
import os
模式
motoko
// 优化前
func f(x : T) : U { return <expr>; };

// 优化后
func f(x : T) : U { <expr> };
注意事项
  • 仅当函数体仅包含单个
    return <expr>;
    语句时应用此优化。
  • 不要转换多语句函数体,或包含
    try
    label
    switch
    await
    导致控制流不同的函数体。
  • 包含多个
    return
    语句的函数(例如
    switch
    分支中的提前返回
    return null
    )绝对不能移除任何return语句。
  • 单个非终结
    return
    (提前/条件退出)
    :如果函数仅有一个
    return
    但它不是函数体的最终语句(例如
    if
    switch
    分支中的提前返回,后续还有一个默认的返回表达式),则函数有两个不同的返回路径。不要移除提前的
    return
    ,反而要给最终的表达式添加显式的
    return
    ,使函数共有两个
    return
    关键字——每个退出路径对应一个。这样能让两个控制流退出路径都清晰且一致。
    motoko
    // 优化前——一个显式return,一个隐式默认返回
    func lookup(k : Key) : ?V {
      if (cache.contains(k)) { return cache.get(k) };
      table.find(k)
    };
    
    // 优化后——两个退出路径都使用`return`
    func lookup(k : Key) : ?V {
      if (cache.contains(k)) { return cache.get(k) };
      return table.find(k);
    };
  • 函数末尾的
    return switch (...) { ... }
    是允许的
    。每个分支块以要返回的表达式结尾(分支内无
    return
    关键字)。当所有分支正常生成值时,这是首选风格。
    motoko
    // 允许——所有分支生成值,无陷阱或抛出异常
    return switch (decode(data)) {
      case (#ok key) { (key.x, key.y) };
      case (#err msg) { #err(msg) };
    };
  • 例外情况:如果任何分支包含等效于return的语句(
    Runtime.trap
    throw
    )表示真正的函数退出,则将
    return
    移到分支块内部
    。移除
    switch
    前的
    return
    ,然后仅在生成值的分支块中添加
    return
    。包含
    Runtime.trap
    throw
    的分支不需要
    return
    。这样能明确哪些分支返回值,哪些分支终止执行:
    motoko
    // 避免——trap隐藏在return switch中
    return switch (decode(data)) {
      case (#ok key) { (key.x, key.y) };
      case (#err msg) { Runtime.trap(msg) };
    };
    
    // 首选——仅在生成值的分支中使用return
    switch (decode(data)) {
      case (#ok key) { return (key.x, key.y) };
      case (#err msg) { Runtime.trap(msg) };
    };
  • 不可达的trap不等同于return。如果使用
    Runtime.trap("unreachable")
    ,或注释/消息表明该分支仅在出现bug时才会进入,那么该trap不是正常的函数退出——而是一个防御性断言。这种情况下,
    return switch
    是可行的,不应将
    return
    移到内部:
    motoko
    // 允许——trap仅用于防护不可能的分支
    return switch (Jacobi.fromNat(x, y, 1, curve)) {
      case (null) Runtime.trap("unreachable");
      case (?point) point;
    };
自动化示例
  • 查找候选函数:
    grep -rn "func \\\\w\\\\+(.*) *:.*{ *return .*; *};" . --include="*.mo" | grep -v \\.mops
  • 检查匹配结果;然后使用编辑器或脚本替换工具应用优化。
脚本安全要求(从实际迁移中总结)
  • 简单的基于行的return计数器是不够的。必须以字符级别跟踪函数边界,解析大括号深度。
  • 嵌套函数:当函数体包含嵌套的
    func
    声明时,完全跳过嵌套函数的函数体——仅统计最外层函数作用域内的return语句。
  • 准确的函数边界检测:使用字符级别扫描,处理字符串字面量(跳过
    "..."
    包括
    \\"
    转义)、注释(
    //
    行注释和
    /* ... */
    块注释),并跟踪大括号深度以找到每个函数真正的闭合
    }
  • 计数规则:统计最外层函数体内任何位置的每个
    return
    ,包括嵌套在
    switch
    if
    for
    while
    等控制流块内的
    return
    ,但排除嵌套
    func
    体内的
    return
    。只有当总数恰好为1,且该单个
    return
    是函数体的终结直接语句(闭合
    }
    之前只能有空白、注释和可选的
    ;
    )时,函数才可以安全重写。如果单个
    return
    是提前/条件退出,函数还有一个额外的隐式返回路径(默认的最终表达式)——保留两者不变(脚本不会移除它;请根据上述风格说明手动给默认表达式添加显式
    return
    )。
经过实战检验的Python脚本
将脚本保存为项目根目录下的
remove_returns.py
,运行
python3 remove_returns.py
,然后删除脚本。
python
#!/usr/bin/env python3
"""
Remove terminal `return` from Motoko functions that have exactly one return
statement in their direct body (excluding nested functions).

Usage:
  1. Set src_dirs to match your project layout.
  2. Run: python3 remove_returns.py
  3. Run tests: npx mops test
  4. Delete this script after confirming all tests pass.

Safety:
  - Tracks function boundaries via character-level brace-depth parsing.
  - Skips string literals ("..." with \\" escapes) and comments (// and /* */).
  - Detects nested `func` declarations and skips their bodies entirely.
  - Only removes the `return` keyword (+ trailing space) from functions with
    exactly 1 return at any depth within the direct body.
"""

import re
import glob
import os

── Configuration ──────────────────────────────────────────────────────

── Configuration ──────────────────────────────────────────────────────

Directories to process (relative to script location or cwd).

Directories to process (relative to script location or cwd).

SRC_DIRS = ["src", "test", "bench"]
SRC_DIRS = ["src", "test", "bench"]

──────────────────────────────────────────────────────────────────────

──────────────────────────────────────────────────────────────────────

def find_func_bodies(text): """ Yield (body_start, body_end) for every top-level and nested function found in
text
. body_start is the index of the opening '{' of the function body; body_end is the index of the matching closing '}'.
Handles:
  - String literals (skips content inside "...")
  - Line comments (// ...)
  - Block comments (/* ... */)
  - Nested braces
"""
i = 0
n = len(text)
while i < n:
    # Skip string literals
    if text[i] == '"':
        i += 1
        while i < n and text[i] != '"':
            if text[i] == '\\':
                i += 1  # skip escaped char
            i += 1
        i += 1  # skip closing "
        continue

    # Skip line comments
    if text[i] == '/' and i + 1 < n and text[i + 1] == '/':
        i += 2
        while i < n and text[i] != '\n':
            i += 1
        continue

    # Skip block comments
    if text[i] == '/' and i + 1 < n and text[i + 1] == '*':
        i += 2
        while i < n and not (text[i] == '*' and i + 1 < n and text[i + 1] == '/'):
            i += 1
        i += 2  # skip */
        continue

    # Look for 'func' keyword at a word boundary
    if text[i:i+4] == 'func' and (i == 0 or not text[i-1].isalnum() and text[i-1] != '_'):
        after = text[i+4:i+5] if i + 4 < n else ''
        if after == '' or not (after.isalnum() or after == '_'):
            # Found a func keyword. Scan forward to find the opening '{'.
            j = i + 4
            while j < n:
                if text[j] == '"':
                    j += 1
                    while j < n and text[j] != '"':
                        if text[j] == '\\':
                            j += 1
                        j += 1
                    j += 1
                    continue
                if text[j] == '{':
                    # Found the opening brace of the function body.
                    brace_start = j
                    depth = 1
                    j += 1
                    while j < n and depth > 0:
                        if text[j] == '"':
                            j += 1
                            while j < n and text[j] != '"':
                                if text[j] == '\\':
                                    j += 1
                                j += 1
                            j += 1
                            continue
                        if text[j] == '/' and j + 1 < n and text[j + 1] == '/':
                            j += 2
                            while j < n and text[j] != '\n':
                                j += 1
                            continue
                        if text[j] == '/' and j + 1 < n and text[j + 1] == '*':
                            j += 2
                            while j < n and not (text[j] == '*' and j + 1 < n and text[j + 1] == '/'):
                                j += 1
                            j += 2
                            continue
                        if text[j] == '{':
                            depth += 1
                        elif text[j] == '}':
                            depth -= 1
                        j += 1
                    brace_end = j - 1  # index of closing '}'
                    yield (brace_start, brace_end)
                    i = j
                    break
                if text[j] == '=' or text[j] == ';':
                    # func ... = expr; (no body) or forward decl
                    i = j + 1
                    break
                j += 1
            else:
                i = j
            continue
    i += 1
def count_returns_in_direct_body(text, body_start, body_end): """ Count
return
statements that are directly inside this function body (not inside nested functions). Returns list of (return_keyword_start, return_keyword_end) positions. """ body = text[body_start + 1 : body_end] # content between { and } offset = body_start + 1
# First, find all nested func bodies within this body so we can skip them.
nested_ranges = []
for ns, ne in find_func_bodies(body):
    # Adjust to absolute positions
    nested_ranges.append((ns + offset, ne + offset))

def is_inside_nested(pos):
    for ns, ne in nested_ranges:
        if ns <= pos <= ne:
            return True
    return False

# Now scan for `return` keywords in the body, skipping nested funcs.
returns = []
i = 0
while i < len(body):
    # Skip strings
    if body[i] == '"':
        i += 1
        while i < len(body) and body[i] != '"':
            if body[i] == '\\':
                i += 1
            i += 1
        i += 1
        continue

    # Skip line comments
    if body[i] == '/' and i + 1 < len(body) and body[i + 1] == '/':
        i += 2
        while i < len(body) and body[i] != '\n':
            i += 1
        continue

    # Skip block comments
    if body[i] == '/' and i + 1 < len(body) and body[i + 1] == '*':
        i += 2
        while i < len(body) and not (body[i] == '*' and i + 1 < len(body) and body[i + 1] == '/'):
            i += 1
        i += 2
        continue

    # Check for 'return' keyword
    if body[i:i+6] == 'return' and (i == 0 or not body[i-1].isalnum() and body[i-1] != '_'):
        after = body[i+6:i+7] if i + 6 < len(body) else ''
        if after == '' or not (after.isalnum() or after == '_'):
            abs_pos = i + offset
            if not is_inside_nested(abs_pos):
                returns.append((abs_pos, abs_pos + 6))
            i += 6
            continue
    i += 1

return returns
def process_file(filepath): with open(filepath, 'r') as f: text = f.read()
original = text
removals = 0

# Collect all function bodies
func_bodies = list(find_func_bodies(text))

# For each function, check if it has exactly 1 return in its direct body
# Process in reverse order to preserve indices when editing
edits = []  # list of (start, end) of "return " to remove

for body_start, body_end in func_bodies:
    returns = count_returns_in_direct_body(text, body_start, body_end)
    if len(returns) == 1:
        ret_start, ret_end = returns[0]
        # Verify this return is the terminal statement of the direct body:
        # scan past the return's expression (tracking brackets/strings/comments)
        # to its terminating ';' (or body_end), then ensure only whitespace,
        # comments, and optional semicolons remain before body_end.
        n = len(text)
        j = ret_end
        depth = 0
        stmt_end = body_end  # position after the return statement
        while j < body_end:
            c = text[j]
            if c == '"':
                j += 1
                while j < body_end and text[j] != '"':
                    if text[j] == '\\':
                        j += 1
                    j += 1
                j += 1
                continue
            if c == '/' and j + 1 < body_end and text[j + 1] == '/':
                j += 2
                while j < body_end and text[j] != '\n':
                    j += 1
                continue
            if c == '/' and j + 1 < body_end and text[j + 1] == '*':
                j += 2
                while j < body_end and not (text[j] == '*' and j + 1 < body_end and text[j + 1] == '/'):
                    j += 1
                j += 2
                continue
            if c in '({[':
                depth += 1
            elif c in ')}]':
                depth -= 1
            elif c == ';' and depth == 0:
                stmt_end = j + 1
                break
            elif c == '\n' and depth == 0:
                # Statement terminated by newline (no semicolon)
                stmt_end = j
                break
            j += 1
        else:
            stmt_end = body_end

        # Now skip whitespace, comments, and stray semicolons after the return
        k = stmt_end
        terminal = True
        while k < body_end:
            c = text[k]
            if c.isspace() or c == ';':
                k += 1
                continue
            if c == '/' and k + 1 < body_end and text[k + 1] == '/':
                k += 2
                while k < body_end and text[k] != '\n':
                    k += 1
                continue
            if c == '/' and k + 1 < body_end and text[k + 1] == '*':
                k += 2
                while k < body_end and not (text[k] == '*' and k + 1 < body_end and text[k + 1] == '/'):
                    k += 1
                k += 2
                continue
            # Found other code after the return — not terminal.
            terminal = False
            break

        if not terminal:
            continue

        # Remove "return " (keyword + trailing space)
        if ret_end < len(text) and text[ret_end] == ' ':
            edits.append((ret_start, ret_end + 1))
        else:
            edits.append((ret_start, ret_end))

# Apply edits in reverse order to preserve positions
edits.sort(key=lambda x: x[0], reverse=True)
for start, end in edits:
    text = text[:start] + text[end:]
    removals += 1

if text != original:
    with open(filepath, 'w') as f:
        f.write(text)

return removals
def main(): total = 0 files_modified = 0 for src_dir in SRC_DIRS: for filepath in sorted(glob.glob(os.path.join(src_dir, "**/*.mo"), recursive=True)): r = process_file(filepath) if r > 0: print(f" {filepath}: {r} returns removed") total += r files_modified += 1 print(f"\nTotal: {total} returns removed in {files_modified} files")
if name == 'main': main()

---
def find_func_bodies(text): """ Yield (body_start, body_end) for every top-level and nested function found in
text
. body_start is the index of the opening '{' of the function body; body_end is the index of the matching closing '}'.
Handles:
  - String literals (skips content inside "...")
  - Line comments (// ...)
  - Block comments (/* ... */)
  - Nested braces
"""
i = 0
n = len(text)
while i < n:
    # Skip string literals
    if text[i] == '"':
        i += 1
        while i < n and text[i] != '"':
            if text[i] == '\\\\':
                i += 1  # skip escaped char
            i += 1
        i += 1  # skip closing "
        continue

    # Skip line comments
    if text[i] == '/' and i + 1 < n and text[i + 1] == '/':
        i += 2
        while i < n and text[i] != '\
': i += 1 continue
    # Skip block comments
    if text[i] == '/' and i + 1 < n and text[i + 1] == '*':
        i += 2
        while i < n and not (text[i] == '*' and i + 1 < n and text[i + 1] == '/'):
            i += 1
        i += 2  # skip */
        continue

    # Look for 'func' keyword at a word boundary
    if text[i:i+4] == 'func' and (i == 0 or not text[i-1].isalnum() and text[i-1] != '_'):
        after = text[i+4:i+5] if i + 4 < n else ''
        if after == '' or not (after.isalnum() or after == '_'):
            # Found a func keyword. Scan forward to find the opening '{'.
            j = i + 4
            while j < n:
                if text[j] == '"':
                    j += 1
                    while j < n and text[j] != '"':
                        if text[j] == '\\\\':
                            j += 1
                        j += 1
                    j += 1
                    continue
                if text[j] == '{':
                    # Found the opening brace of the function body.
                    brace_start = j
                    depth = 1
                    j += 1
                    while j < n and depth > 0:
                        if text[j] == '"':
                            j += 1
                            while j < n and text[j] != '"':
                                if text[j] == '\\\\':
                                    j += 1
                                j += 1
                            j += 1
                            continue
                        if text[j] == '/' and j + 1 < n and text[j + 1] == '/':
                            j += 2
                            while j < n and text[j] != '\
': j += 1 continue if text[j] == '/' and j + 1 < n and text[j + 1] == '': j += 2 while j < n and not (text[j] == '' and j + 1 < n and text[j + 1] == '/'): j += 1 j += 2 continue if text[j] == '{': depth += 1 elif text[j] == '}': depth -= 1 j += 1 brace_end = j - 1 # index of closing '}' yield (brace_start, brace_end) i = j break if text[j] == '=' or text[j] == ';': # func ... = expr; (no body) or forward decl i = j + 1 break j += 1 else: i = j continue i += 1
def count_returns_in_direct_body(text, body_start, body_end): """ Count
return
statements that are directly inside this function body (not inside nested functions). Returns list of (return_keyword_start, return_keyword_end) positions. """ body = text[body_start + 1 : body_end] # content between { and } offset = body_start + 1
# First, find all nested func bodies within this body so we can skip them.
nested_ranges = []
for ns, ne in find_func_bodies(body):
    # Adjust to absolute positions
    nested_ranges.append((ns + offset, ne + offset))

def is_inside_nested(pos):
    for ns, ne in nested_ranges:
        if ns <= pos <= ne:
            return True
    return False

# Now scan for `return` keywords in the body, skipping nested funcs.
returns = []
i = 0
while i < len(body):
    # Skip strings
    if body[i] == '"':
        i += 1
        while i < len(body) and body[i] != '"':
            if body[i] == '\\\\':
                i += 1
            i += 1
        i += 1
        continue

    # Skip line comments
    if body[i] == '/' and i + 1 < len(body) and body[i + 1] == '/':
        i += 2
        while i < len(body) and body[i] != '\
': i += 1 continue
    # Skip block comments
    if body[i] == '/' and i + 1 < len(body) and body[i + 1] == '*':
        i += 2
        while i < len(body) and not (body[i] == '*' and i + 1 < len(body) and body[i + 1] == '/'):
            i += 1
        i += 2
        continue

    # Check for 'return' keyword
    if body[i:i+6] == 'return' and (i == 0 or not body[i-1].isalnum() and body[i-1] != '_'):
        after = body[i+6:i+7] if i + 6 < len(body) else ''
        if after == '' or not (after.isalnum() or after == '_'):
            abs_pos = i + offset
            if not is_inside_nested(abs_pos):
                returns.append((abs_pos, abs_pos + 6))
            i += 6
            continue
    i += 1

return returns
def process_file(filepath): with open(filepath, 'r') as f: text = f.read()
original = text
removals = 0

# Collect all function bodies
func_bodies = list(find_func_bodies(text))

# For each function, check if it has exactly 1 return in its direct body
# Process in reverse order to preserve indices when editing
edits = []  # list of (start, end) of "return " to remove

for body_start, body_end in func_bodies:
    returns = count_returns_in_direct_body(text, body_start, body_end)
    if len(returns) == 1:
        ret_start, ret_end = returns[0]
        # Verify this return is the terminal statement of the direct body:
        # scan past the return's expression (tracking brackets/strings/comments)
        # to its terminating ';' (or body_end), then ensure only whitespace,
        # comments, and optional semicolons remain before body_end.
        n = len(text)
        j = ret_end
        depth = 0
        stmt_end = body_end  # position after the return statement
        while j < body_end:
            c = text[j]
            if c == '"':
                j += 1
                while j < body_end and text[j] != '"':
                    if text[j] == '\\\\':
                        j += 1
                    j += 1
                j += 1
                continue
            if c == '/' and j + 1 < body_end and text[j + 1] == '/':
                j += 2
                while j < body_end and text[j] != '\
': j += 1 continue if c == '/' and j + 1 < body_end and text[j + 1] == '': j += 2 while j < body_end and not (text[j] == '' and j + 1 < body_end and text[j + 1] == '/'): j += 1 j += 2 continue if c in '({[': depth += 1 elif c in ')}]': depth -= 1 elif c == ';' and depth == 0: stmt_end = j + 1 break elif c == '
' and depth == 0: # Statement terminated by newline (no semicolon) stmt_end = j break j += 1 else: stmt_end = body_end
        # Now skip whitespace, comments, and stray semicolons after the return
        k = stmt_end
        terminal = True
        while k < body_end:
            c = text[k]
            if c.isspace() or c == ';':
                k += 1
                continue
            if c == '/' and k + 1 < body_end and text[k + 1] == '/':
                k += 2
                while k < body_end and text[k] != '\
': k += 1 continue if c == '/' and k + 1 < body_end and text[k + 1] == '': k += 2 while k < body_end and not (text[k] == '' and k + 1 < body_end and text[k + 1] == '/'): k += 1 k += 2 continue # Found other code after the return — not terminal. terminal = False break
        if not terminal:
            continue

        # Remove "return " (keyword + trailing space)
        if ret_end < len(text) and text[ret_end] == ' ':
            edits.append((ret_start, ret_end + 1))
        else:
            edits.append((ret_start, ret_end))

# Apply edits in reverse order to preserve positions
edits.sort(key=lambda x: x[0], reverse=True)
for start, end in edits:
    text = text[:start] + text[end:]
    removals += 1

if text != original:
    with open(filepath, 'w') as f:
        f.write(text)

return removals
def main(): total = 0 files_modified = 0 for src_dir in SRC_DIRS: for filepath in sorted(glob.glob(os.path.join(src_dir, "**/*.mo"), recursive=True)): r = process_file(filepath) if r > 0: print(f" {filepath}: {r} returns removed") total += r files_modified += 1 print(f"
Total: {total} returns removed in {files_modified} files")
if name == 'main': main()

---

B) Dot‑notation conversion

B) 点表示法转换

For all Motoko dot‑notation rules, automation scripts, and pitfalls, see the dedicated skill:
  • skills/dot-notation-migration/SKILL.md
This file intentionally does not duplicate those instructions. Apply dot‑notation changes using the dedicated skill, then continue here with import cleanup (Section D) and import ordering (Section F).

关于Motoko点表示法的所有规则、自动化脚本和注意事项,请参考专门的技能文档:
  • skills/dot-notation-migration/SKILL.md
本文档不会重复这些说明。请使用专门的技能完成点表示法变更,然后回到本文档继续进行导入清理(D部分)和导入排序(F部分)。

C) Dot‑notation import requirements

C) 点表示法的导入要求

For import mapping and rules related to dot‑notation, use the dedicated skill:
  • skills/dot-notation-migration/SKILL.md
This file intentionally does not duplicate the import mapping. After applying dot‑notation changes per that skill, proceed with Section D (unused import cleanup) and Section F (import ordering).

关于点表示法的导入映射和规则,请使用专门的技能文档:
  • skills/dot-notation-migration/SKILL.md
本文档不会重复导入映射内容。根据该技能完成点表示法变更后,继续进行D部分(未使用导入清理)和F部分(导入排序)。

D) Clean up unused imports (safely)

D) 安全清理未使用的导入

Goal
  • Remove imports that are truly unused after prior refactors, but do not remove modules implicitly required by dot‑notation.
Reality check
  • Editor tooling (VSCode Motoko extension) correctly marks unused imports, including dot‑notation awareness. CLI detection can be trickier.
Common false positives (imports that LOOK unused but are REQUIRED)
  • Blob
    — needed when
    .toArray()
    ,
    .size()
    ,
    .isEmpty()
    ,
    .hash()
    are called on
    Blob
    values (e.g.,
    Sha256.fromArray(...).toArray()
    ,
    hmac.sum().toArray()
    )
  • Array
    — needed when
    .flatten()
    ,
    .foldLeft()
    ,
    .sliceToArray()
    ,
    .map()
    ,
    .filter()
    etc. are called on
    [T]
    values (e.g.,
    [arr1, arr2].flatten()
    )
  • Nat
    — needed when
    .toText()
    is called on
    Nat
    values from
    .size()
    (e.g.,
    arr.size().toText()
    )
  • VarArray
    — needed when
    .toArray()
    is called on
    [var T]
    values
  • Rule: If ANY dot-notation method is called on a value of that module's type, the import is required even though the module name never appears explicitly in the code.
Approaches
  1. Editor‑guided
    • Open the workspace in VSCode. For each
      *.mo
      file, accept quick‑fix to remove imports marked as unused. Review diffs.
  2. Compiler/LSP‑assisted batch
    • Use the Motoko language server via the VSCode extension to surface all diagnostics; apply code actions in batches where supported.
  3. Script‑assisted conservative removal
    • Write a simple script that:
      • Parses each
        import ... "mo:core/XYZ";
      • Searches file for either
        XYZ.
        or any of the known dot‑patterns mapped to
        XYZ
        (see Dot‑Notation Migration Skill import mapping)
      • If neither is found, flag the line as removable
    • Manually review flagged lines before deletion
Audit helpers
  • After cleanup, search for "import" lines whose module name never appears and no mapped dot‑pattern is present.
  • Build the project. If a required module was removed, dot‑calls will fail at compile time — restore import and refine rules.

目标
  • 移除之前重构后真正未使用的导入,但不要移除点表示法隐式依赖的模块。
实际情况
  • 编辑器工具(VSCode Motoko扩展)能正确标记未使用的导入,包括对点表示法的识别。CLI检测则更复杂。
常见误判(看起来未使用但实际必需的导入)
  • Blob
    ——当对
    Blob
    值调用
    .toArray()
    .size()
    .isEmpty()
    .hash()
    时需要(例如
    Sha256.fromArray(...).toArray()
    hmac.sum().toArray()
  • Array
    ——当对
    [T]
    值调用
    .flatten()
    .foldLeft()
    .sliceToArray()
    .map()
    .filter()
    等方法时需要(例如
    [arr1, arr2].flatten()
  • Nat
    ——当对
    .size()
    返回的
    Nat
    值调用
    .toText()
    时需要(例如
    arr.size().toText()
  • VarArray
    ——当对
    [var T]
    值调用
    .toArray()
    时需要
  • 规则:如果对该模块类型的任何值调用了点表示法方法,即使代码中从未显式出现模块名称,该导入也是必需的。
方法
  1. 编辑器引导
    • 在VSCode中打开工作区。对每个
      *.mo
      文件,接受快速修复以移除标记为未使用的导入。检查差异。
  2. 编译器/LSP辅助批量处理
    • 通过VSCode扩展使用Motoko语言服务器显示所有诊断信息;在支持的情况下批量应用代码操作。
  3. 脚本辅助的保守移除
    • 编写一个简单脚本:
      • 解析每个
        import ... "mo:core/XYZ";
        语句
      • 在文件中搜索
        XYZ.
        或任何映射到
        XYZ
        的已知点模式(参考点表示法迁移技能的导入映射)
      • 如果两者都未找到,则标记该行可移除
    • 删除前手动检查标记的行
审计辅助工具
  • 清理完成后,搜索模块名称从未出现且无对应点模式的"import"行。
  • 构建项目。如果必需的模块被移除,点调用会在编译时失败——恢复导入并优化规则。

E) Shorten local (sibling) import paths

E) 缩短本地(同级)导入路径

  • Use bare module names for local imports when possible:
    "Bech32"
    instead of
    "./Bech32"
    . Both resolve correctly, but bare names are more concise and idiomatic.
    motoko
    // Good
    import Bech32 "Bech32";
    import Script "Script";
    import Types "Types";
    
    // Acceptable (cross-directory)
    import ByteUtils "../ByteUtils";
    import Curves "../ec/Curves";
    
    // Avoid (unnecessary ./ prefix for siblings)
    import Bech32 "./Bech32";
  • For cross-directory imports, relative paths with
    ../
    are required and acceptable.

  • 尽可能对本地导入使用裸模块名:
    "Bech32"
    而非
    "./Bech32"
    。两种方式都能正确解析,但裸模块名更简洁且符合惯用风格。
    motoko
    // 推荐
    import Bech32 "Bech32";
    import Script "Script";
    import Types "Types";
    
    // 可接受(跨目录)
    import ByteUtils "../ByteUtils";
    import Curves "../ec/Curves";
    
    // 避免(同级导入无需./前缀)
    import Bech32 "./Bech32";
  • 对于跨目录导入,带
    ../
    的相对路径是必需且可接受的。

F) Aggregate and alphabetize imports by section

F) 按部分聚合并按字母排序导入

Why
  • Consistent ordering reduces merge conflicts and speeds reviews. Clear grouping improves scanning and avoids mixing external modules with local ones.
Sections (in this order, each separated by a single blank line)
  1. mo:core imports
    • All imports whose path starts with "mo:core/..." (including
      mo:core/Types
      ).
  2. Other mo:* third‑party imports (mops or similar)
    • Any
      mo:...
      imports that are not
      mo:core/...
      (e.g.,
      mo:uuid/UUID
      ,
      mo:sha2/SHA256
      , etc.).
  3. Local project modules
    • Bare module name imports like
      "Bech32"
      ,
      "Common"
      (preferred), or relative path imports like
      "../ByteUtils"
      ,
      "./Script"
      .
    • Prefer bare module names without
      ./
      prefix for sibling imports (e.g.,
      "Bech32"
      instead of
      "./Bech32"
      ). Both work, but bare names are cleaner.
    • Sort local project module imports by the local name they are imported as, not by the module name in the imported path.
Sorting rules (apply within each section independently)
  • Sort alphabetically by the local name modules are imported as
  • Preserve import style (module vs. named type imports).
  • Keep multiple named‑type imports from the same path on a single line as‑is.
  • Optionally keep a comment header above each section (Core, Third‑party, Local) if your repo style prefers.
Example
motoko
// Before (mixed)
import Runtime "mo:core/Runtime";
import { type Result } "mo:core/Types";
import SHA256 "mo:sha2/SHA256";
import Map "mo:core/Map";
import BitVec "mo:bitvec/BitVec";
import Utils "../lib/Utils";
import Logger "./Logger";

// After (aggregated and sorted per section)
//// Core
import Map "mo:core/Map";
import Runtime "mo:core/Runtime";
import { type Result } "mo:core/Types";

//// Third‑party (mops)
import BitVec "mo:bitvec/BitVec";
import SHA256 "mo:sha2/SHA256";

//// Local
import Logger "Logger";
import Utils "../lib/Utils";
Lightweight automation idea (per file)
  • Collect all import lines at the file top.
  • Partition into the three sections by path prefix.
  • Sort each partition alphabetically by the local name they are imported as
  • Re‑emit sections in the order Core → Third‑party → Local, with a blank line between sections.
  • Keep any non‑import comments at their relative positions unless they clearly belong to a section header.

原因
  • 一致的排序减少合并冲突,加快审核速度。清晰的分组提升扫描效率,避免外部模块与本地模块混合。
部分(按此顺序,各部分之间用一个空行分隔)
  1. mo:core导入
    • 所有路径以"mo:core/..."开头的导入(包括
      mo:core/Types
      )。
  2. 其他mo:*第三方导入(mops或类似源)
    • 所有非
      mo:core/...
      mo:...
      导入(例如
      mo:uuid/UUID
      mo:sha2/SHA256
      等)。
  3. 本地项目模块
    • 裸模块名导入如
      "Bech32"
      "Common"
      (推荐),或相对路径导入如
      "../ByteUtils"
      "./Script"
    • 同级导入优先使用不带
      ./
      前缀的裸模块名(例如
      "Bech32"
      而非
      "./Bech32"
      )。两种方式都可行,但裸模块名更简洁。
    • 本地项目模块导入按导入的本地名称排序,而非导入路径中的模块名。
排序规则(在每个部分内独立应用)
  • 按导入的本地名称字母排序
  • 保留导入风格(模块导入 vs 命名类型导入)。
  • 同一路径的多个命名类型导入保持在同一行不变。
  • 如果你的仓库风格偏好,可在每个部分上方保留注释标题(Core、Third-party、Local)。
示例
motoko
// 优化前(混合)
import Runtime "mo:core/Runtime";
import { type Result } "mo:core/Types";
import SHA256 "mo:sha2/SHA256";
import Map "mo:core/Map";
import BitVec "mo:bitvec/BitVec";
import Utils "../lib/Utils";
import Logger "./Logger";

// 优化后(按部分聚合并排序)
//// Core
import Map "mo:core/Map";
import Runtime "mo:core/Runtime";
import { type Result } "mo:core/Types";

//// Third‑party (mops)
import BitVec "mo:bitvec/BitVec";
import SHA256 "mo:sha2/SHA256";

//// Local
import Logger "Logger";
import Utils "../lib/Utils";
轻量级自动化思路(按文件处理)
  • 收集文件顶部的所有import行。
  • 按路径前缀分为三个部分。
  • 每个部分按导入的本地名称字母排序
  • 按Core → Third-party mo:* → Local的顺序重新输出各部分,部分之间插入空行。
  • 保留非导入注释的相对位置,除非它们明显属于某个部分标题。

G) Direct string‑to‑Blob assignment for constants

G) 常量的直接字符串到Blob赋值

Pattern
motoko
// Before
let b : Blob = Text.encodeUtf8("hello");

// After
let b : Blob = "hello";
Why
  • For constant Text strings, the Motoko compiler allows direct assignment to the
    Blob
    type.
  • The result is identical to
    Text.encodeUtf8
    , but the code is cleaner and avoids an explicit function call.
Examples
motoko
// Good: direct assignment
let blobs = [
  "strategy",
  Text.encodeUtf8(Nat.toText(slot)),
];

// Avoid: redundant encoding for constant string
let blobs = [
  Text.encodeUtf8("strategy"),
  Text.encodeUtf8(Nat.toText(slot)),
];

模式
motoko
// 优化前
let b : Blob = Text.encodeUtf8("hello");

// 优化后
let b : Blob = "hello";
原因
  • 对于常量Text字符串,Motoko编译器允许直接赋值给
    Blob
    类型。
  • 结果与
    Text.encodeUtf8
    完全相同,但代码更简洁,无需显式函数调用。
示例
motoko
// 推荐:直接赋值
let blobs = [
  "strategy",
  Text.encodeUtf8(Nat.toText(slot)),
];

// 避免:常量字符串的冗余编码
let blobs = [
  Text.encodeUtf8("strategy"),
  Text.encodeUtf8(Nat.toText(slot)),
];

Practical Automation Recipes (opt‑in)

实用自动化方案(可选)

These are optional starting points. Prefer editor‑integrated refactors when available. Always review diffs.
  1. Find one‑line return functions
bash
rg -n --glob '!**/.mops/**' --glob '**/*.mo' "func [A-Za-z_][A-Za-z0-9_]*\(.*\) *:.*\{ *return .*; *};"
  1. Dot‑notation conversion & candidate detection
  • See the dedicated skill for full automation and grep recipes:
    • skills/dot-notation-migration/SKILL.md
  1. Flag possibly unused core imports (conservative)
bash
undefined
这些是可选的起点。优先使用编辑器集成的重构工具。始终检查差异。
  1. 查找单行return函数
bash
rg -n --glob '!**/.mops/**' --glob '**/*.mo' "func [A-Za-z_][A-Za-z0-9_]*\\(.*\\) *:.*\\{ *return .*; *};"
  1. 点表示法转换与候选检测
  • 完整的自动化和grep方案请参考专门的技能文档:
    • skills/dot-notation-migration/SKILL.md
  1. 标记可能未使用的core导入(保守策略)
bash
undefined

Rough heuristic: list imports, then search for name or dot‑patterns

粗略的启发式方法:列出导入,然后搜索名称或点模式

rg -n --glob '!/.mops/' --glob '**/.mo' '^import ."mo:core/([A-Za-z/]+)";' -o -r '$1'
rg -n --glob '!/.mops/' --glob '**/.mo' '^import ."mo:core/([A-Za-z/]+)";' -o -r '$1'

For each file, ensure presence of module references OR mapped dot‑patterns before removal

对每个文件,在移除前确保存在模块引用或对应的点模式


4) Aggregate + sort imports into sections (editor macro)
- Select all `import` lines at the top of the file → group into three sections (Core, Third‑party mo:*, Local) → sort each group alphabetically by path → insert blank lines between sections → keep import styles as‑is.

---

4) 聚合+排序导入到各部分(编辑器宏)
- 选择文件顶部的所有`import`行 → 分为三个部分(Core、Third-party mo:*、Local) → 每个部分按路径字母排序 → 部分之间插入空行 → 保留导入风格不变。

---

Agent Strategy (for AI assistants)

AI助手策略

  1. Confirm the project builds on
    mo:core
    before starting improvements.
  2. Work file‑by‑file. For each file:
    • A. Remove single‑expression
      return
      forms
    • B. Apply dot‑notation per skills/dot-notation-migration/SKILL.md
    • C. Ensure required imports for any introduced dot‑notation (see import mapping in skills/dot-notation-migration/SKILL.md)
    • D. Remove truly unused imports (respect the dot‑notation import mapping from the dedicated skill)
    • E. Shorten local (sibling) import paths (remove
      ./
      prefix where applicable)
    • F. Aggregate imports into the three sections and sort each section alphabetically (Core → Third‑party mo:* → Local)
    • G. Replace
      Text.encodeUtf8("<literal>")
      with
      "<literal>"
      where the target type is
      Blob
      .
  3. After each file: compile; if failure due to missing import, restore and mark mapping
  4. After each category across repo: run a full build and optionally tests
  5. Produce a short report of changes and any edge cases deferred for manual review

  1. 开始改进前,确认项目基于
    mo:core
    可构建。
  2. 逐个文件处理。对每个文件:
    • A. 移除单表达式
      return
      形式
    • B. 根据skills/dot-notation-migration/SKILL.md应用点表示法
    • C. 确保任何引入的点表示法所需的导入(参考skills/dot-notation-migration/SKILL.md中的导入映射)
    • D. 移除真正未使用的导入(遵循专门技能中的点表示法导入映射)
    • E. 缩短本地(同级)导入路径(在适用情况下移除
      ./
      前缀)
    • F. 将导入内容归为三个部分并按字母排序(Core → Third-party mo:* → Local)
    • G. 当目标类型为
      Blob
      时,将
      Text.encodeUtf8("<literal>")
      替换为
      "<literal>"
  3. 每个文件处理完成后:编译;如果因缺失导入导致失败,恢复导入并标记映射
  4. 完成仓库中每一类改进后:运行完整构建,可选运行测试
  5. 生成简短的变更报告,以及任何推迟手动审核的边缘情况

H) Convert
Array.fromVarArray(x)
to
x.toArray()

H) 将
Array.fromVarArray(x)
转换为
x.toArray()

Pattern
motoko
// Before
Array.fromVarArray(buf)
Array.fromVarArray<Nat8>(buf)

// After
buf.toArray()
Notes
  • Array.fromVarArray
    is a factory function (first param is NOT
    self
    ), so the dot-notation migration script does NOT convert it automatically. This is a separate conversion.
  • VarArray.toArray()
    is the dot-notation equivalent — it's defined on
    [var T]
    values.
  • After conversion, check if
    Array
    import can be removed (it may still be needed for
    Array.tabulate
    ,
    Array.flatten
    dot-notation on
    [T]
    , etc.)
  • Strip optional type params:
    Array.fromVarArray<Nat8>(buf)
    buf.toArray()
    (the type is inferred from the var array).
模式
motoko
// 优化前
Array.fromVarArray(buf)
Array.fromVarArray<Nat8>(buf)

// 优化后
buf.toArray()
注意事项
  • Array.fromVarArray
    是工厂函数(第一个参数不是
    self
    ),因此点表示法迁移脚本不会自动转换它。这是一个单独的转换操作。
  • VarArray.toArray()
    是点表示法的等效形式——它定义在
    [var T]
    值上。
  • 转换后,检查是否可以移除
    Array
    导入(可能仍需要它用于
    Array.tabulate
    [T]
    上的点表示法
    Array.flatten
    等)
  • 移除可选的类型参数:
    Array.fromVarArray<Nat8>(buf)
    buf.toArray()
    (类型可从可变数组推断)。

I)
Array.tabulate
type annotations are usually required

I)
Array.tabulate
通常需要类型注解

  • Do NOT remove type annotations from
    Array.tabulate<T>(...)
    calls.
  • The Motoko compiler often cannot infer the element type, especially when the callback uses
    fromNat
    , arithmetic, or other expressions that could return multiple numeric types.
  • Removing annotations caused 13 of 24 test files to fail in a real project with errors like
    expression of type [Any] cannot produce expected type [Nat8]
    .
  • Keep them:
    Array.tabulate<Nat8>(n, func i { ... })
    .

  • 不要移除
    Array.tabulate<T>(...)
    调用中的类型注解。
  • Motoko编译器通常无法推断元素类型,尤其是当回调使用
    fromNat
    、算术运算或其他可能返回多种数值类型的表达式时。
  • 在实际项目中,移除注解导致24个测试文件中的13个失败,错误如
    expression of type [Any] cannot produce expected type [Nat8]
  • 保留类型注解:
    Array.tabulate<Nat8>(n, func i { ... })

Edge Cases & Gotchas

边缘情况与注意事项

  • For all dot‑notation behavior, method availability, factories vs methods, and mutability notes, see:
    • skills/dot-notation-migration/SKILL.md
  • When aggregating imports, keep named type imports from
    mo:core/Types
    within the
    mo:core
    group; see Section F for ordering rules.
  • Local imports: prefer bare module names (
    "Bech32"
    ) over relative paths (
    "./Bech32"
    ) for sibling files. Both resolve correctly but bare names are more concise.
  • Import paths like
    "../src/Bech32"
    from within
    src/
    are incorrect — use
    "Bech32"
    for siblings or
    "../SubDir/Module"
    for cross-directory references.

  • 关于点表示法的行为、方法可用性、工厂函数vs方法、可变性说明,请参考:
    • skills/dot-notation-migration/SKILL.md
  • 聚合导入时,将来自
    mo:core/Types
    的命名类型导入保留在
    mo:core
    组内;参考F部分的排序规则。
  • 本地导入:同级文件优先使用裸模块名(
    "Bech32"
    )而非相对路径(
    "./Bech32"
    )。两种方式都能正确解析,但裸模块名更简洁。
  • src/
    内部导入
    "../src/Bech32"
    这类路径是错误的——同级文件使用
    "Bech32"
    ,跨目录引用使用
    "../SubDir/Module"

Verification & Sign‑off

验证与验收

  • Build all canisters successfully after changes.
  • Run static audits:
    • No
      import
      lines flagged unused by editor or heuristic scripts (after accounting for dot‑notation needs).
    • Spot check: arrays, maps, sets, text operations use dot‑notation where natural.
  • Diffs remain mechanical; no public API or behavioral changes.

  • 变更后所有canister成功构建。
  • 运行静态审计:
    • 编辑器或启发式脚本未标记未使用的
      import
      行(已考虑点表示法需求)。
    • 抽查:数组、映射、集合、文本操作在合理场景下使用点表示法。
  • 差异仅为机械性变更;无公共API或行为变化。

Appendix: Dot‑notation reference

附录:点表示法参考

For the complete, maintained dot‑notation catalog, automation scripts, and import mapping, see:
  • skills/dot-notation-migration/SKILL.md
完整的、维护中的点表示法目录、自动化脚本和导入映射,请参考:
  • skills/dot-notation-migration/SKILL.md ",