# TSL 命名偏好

文档类型：风格偏好页
是否可直接用于生成代码：仅部分
是否含可直接照写示例：否
是否含不可照写反例：否
遇到不确定时：命名对象不明看 [syntax/02_core_model.md](syntax/02_core_model.md)；代码组织看 [code_style.md](code_style.md)；语法事实看 [syntax/index.md](syntax/index.md)

本页记录本仓库/作者的命名偏好，不代表 TSL 语法事实。本页只规定命名后的实体如何取名，不承担文件模型、顶层声明、执行顺序等语法判断。命名偏好与 Google C++ Style Guide 的实体形状思路对齐：通过名字的“形状”快速判断实体类型（类型/函数/变量/常量等），减少阅读成本。

以下出现的 `<...>` 形式只表示命名或注释模板占位，不是源码字面量。

## 1. 选名原则

- **可读一致**：名字清晰可读，并随可见范围调整具体程度。
  - 可见范围越大（越对外），名字越应具体、少省略。
  - 本指南中“对外可见”指：`unit interface`、`class public`、顶层 `function`。
- **标识符语言**：标识符（类型/函数/property/变量/参数等）统一使用英文；禁止中文与拼音（注释可中英混合，见
  `docs/tsl/code_style.md`）。
- **少用生僻缩写**：能写全称就写全称。
  - 允许使用团队已约定、大家都懂的常见缩写；若缩写不够通用，优先写全称或在评审/文档中先达成约定。
- **驼峰/帕斯卡中的缩写规则**：缩写（首字母缩写/词组缩写）在
  `PascalCase`/`camelCase`
  中**按一个单词处理**，写成“首字母大写其余小写”，不要写一串全大写。
  - 示例：`UserId`（不是 `UserID`）、`UrlTable`（不是 `URLTable`）、
    `StartRpcServer`（不是 `StartRPCServer`）、`HttpClient`（不是
    `HTTPClient`）。
- **避免无意义词**：如 `data`、`info`、`tmp`、`handle` 等。
  - 可以作为限定词的一部分（例如 `user_data`），但不要单独用作名字（例如仅叫
    `data`）。

## 2. 命名风格总览

对于以下规则，“单词”指英文中不带空格的词。

- `snake_case`：全小写，下划线分隔单词，用于普通变量/参数等；私有类成员变量使用
  `snake_case_`（见 5.2）。
- `PascalCase`（`UpperCamelCase`）：每个单词首字母大写，无下划线，用于类型、顶层函数/“动作型”方法、property，以及（少量）公有成员字段（访问器/设置器方法见 7 的例外约定）。
- 自定义标识符只使用本指南约定的 `PascalCase`/`snake_case`；`lowerCamelCase`
  仅用于沿用内置/标准库/第三方 API 的既有命名。

### 大小写与关键字约定

- TSL 语言大小写无关，但本指南仍要求按约定使用大小写以提升可读性；不要用仅大小写不同的名字区分不同实体；同一标识符在仓库中应保持一致写法。
- 所有语法关键字统一使用全小写书写，例如
  `if`、`for`、`class`、`function`、`unit`、`return` 等。
- 调用内置/标准库/第三方 API 时，推荐保持对方官方大小写形式（`aaBBCC`/lowerCamelCase），例如
  `getSysParams("xxx")`；自定义 wrapper 仍按本指南使用 `PascalCase`。
- 常规自定义标识符不要使用 TSL 关键字、保留字或内置函数名。适用范围包括变量、
  参数、函数/过程、类/类型、property、方法、字段、unit 名和文件基名。
- 关键字和保留名清单以
  [16_lexical_structure_and_compile_options.md](syntax/16_lexical_structure_and_compile_options.md)
  为准；例如 `label` 是控制流保留名，不要作为普通变量、函数或属性名。
- 如果业务术语与关键字或内置名冲突，优先换成语义清楚的替代名称。只有明确需要
  演示同名遮蔽、绑定外部接口或保持协议兼容时，才允许使用冲突名。
- 函数参数场景可以使用 5.1 中的前导下划线例外。

## 3. 类型命名（Type Names）

文件模型和顶层声明规则以 `docs/tsl/syntax/02_core_model.md` 为准。本页只覆盖“需要命名的顶层实体”，不覆盖脚本语句本身。

- **类与单元**使用
  `PascalCase`，不带下划线；名称应为名词/名词短语（通常单数），避免动词开头。
- 顶层类的正式写法统一按 `type Name = class ... end;` 理解；不要把裸 `class`
  当成正式顶层类模型的简称。
- 不推荐 `*Unit` 作为 `unit` 的后缀（`unit`
  本身已表达语义）；需要表达用途时，可使用 `*Shared`/`*Common`/`*Enums`
  等更具体后缀（按团队约定）。
- **顶层函数 / 过程**使用 `PascalCase`；名称优先动词/动词短语（例如
  `Load`/`Parse`/`Build`），详见函数命名章节。
- 示例：`UserAccount`、`OrderShared`、`LoadMarketData()`、`FlushCache()`。

## 4. 文件命名与顶层声明（File Names）

文件模型、可写顶层声明、声明与文件关系以
`docs/tsl/syntax/02_core_model.md` 为准；本节只给已经确定要命名的文件/实体提供命名偏好。

- 先用核心模型页判断目标是否存在需要命名的顶层实体；不要从本页反推
  `.tsl` / `.tsf` 可写结构。
- 需要命名的顶层实体可以是 `function / procedure`、`type Name = class`、`unit`。
- 批量重命名时，把文件基名和顶层实体名作为同一组维护，避免检索和协作断裂。

命名建议：

- 基名统一使用 `PascalCase`，与顶层声明的推荐写法一致。
- 示例：
  - `DailyEntry.tsl` 作为入口 / 编排脚本，主体是松散语句或示例脚本外壳。
  - `UserAccount.tsf` 中定义 `type UserAccount = class ... end;`.
  - `DocxEnumerations.tsf` 中定义 `unit DocxEnumerations; ... end.`
  - `ParseConfig.tsf` 中定义 `function ParseConfig(...)`.
  - `FlushCache.tsf` 中定义 `procedure FlushCache(...);`.

注：TSL 大小写无关，实际编译时按大小写比较不会出错，但仍应保持文件名与声明名的推荐写法一致以便检索与协作。

## 5. 变量命名（Variable Names）

### 5.1 普通变量与参数

- **局部变量、函数参数、非成员变量**使用 `snake_case`。
- 若参数名与 TSL 关键字冲突导致编译失败，使用前导下划线的 `snake_case`
  作为例外，例如 `_type`（`type` 是常见冲突关键字）。
- 前导下划线 `_`
  **仅用于上述关键字冲突的参数场景**，不要用于其他局部变量、成员变量、函数/类型/单元名称或全局变量。
- 建议把单位写进名字（尤其时间/金额/比例）：例如
  `timeout_ms`、`spread_bp`、`ratio_pct`。
- 示例：`table_name`、`max_retry_count`、`user_id`（短名例外见 5.5）。

### 5.2 类成员（Class Data Members）

- **私有成员变量**使用 `snake_case_`（尾随下划线）。
- 尾随下划线 `_`
  **仅用于私有成员变量**，不要用于局部变量、参数、公有成员字段、property 名称或顶层全局变量。
- **公有成员变量**若必须存在，使用
  `PascalCase`；但**不推荐外部直接访问公有字段**。
- 对外暴露的成员优先使用 **property**：
  - property 名称使用 `PascalCase`（视为对外 API）。
  - property 的 `read/write` 指向真实成员（通常为私有 `snake_case_`）。
  - 布尔 property 使用 `Is`/`Has`/`Can`/`Should` 等前缀的 `PascalCase`（例如
    `IsReady`），对应私有成员可用 `is_ready_` 等。
- 示例：

代码块身份：配置片段 / 概念骨架

```tsl
type User = class
public
    property UserId read user_id_ write user_id_;
    property IsReady read is_ready_;
private
    user_id_;
    is_ready_;
end;
```

### 5.3 全局/静态变量

- 不推荐使用顶层全局/静态可变变量；优先封装到 `unit`/`class`
  中，通过函数或 property 访问。
- 合理例外（仍需集中管理，避免到处读写）：
  - 进程级只读配置缓存（启动后不再变）。
  - 有上限/可清理的缓存（明确容量/TTL/清理点）。
  - 指标/计数器（只增不减，或集中在少数写入点更新）。
- 若必须声明顶层全局/静态可变变量：
  - 命名仍使用 `snake_case`（不使用 `g_` 前缀）。
  - 必须在声明处写注释说明：它是什么、用于什么、以及（如不明显）为什么需要是全局/静态。
  - 建议补充写入点与生命周期：谁会写、何时写、何时清理/重置；如涉及并发，写明并发假设/保护方式。
  - 不要在注释/日志中写入任何敏感信息；如需标识调用上下文，优先使用脱敏后的业务 ID、任务 ID 或路径键。
- 示例（注释模板，按需裁剪）：
  - 以下为注释模板占位，不是源码字面量。

代码块身份：配置片段 / 概念骨架

```tsl
// <var_name>: <what it is>
// Used for: <what it is used for>
// Global because: <why it needs to be global (if unclear)>
```

- 全局/静态常量仍按常量规则使用
  `kPascalCase`（建议同样写一句用途注释，便于检索与维护）。

### 5.4 布尔变量

- 布尔变量建议区分两类语义：
  - **状态/谓词（predicate）**：描述“是否满足某条件/是否处于某状态”，使用
    `is_ / has_ / can_ / should_` 等前缀表达语义。
    - 示例：`is_ready`、`has_error`、`can_retry`。
  - **选项/开关（flag/option）**：描述“是否启用某行为/模式”，允许使用不带 `is_`
    的 `snake_case` 短语（更贴近配置项语义）。
    - 示例：`dry_run`、`include_header`、`enable_cache`、`use_cache`。
- 尽量使用正向命名，避免双重否定：`is_valid` 优于
  `is_not_valid`；`disable_cache` 这类命名需谨慎（容易在调用点读错）。

### 5.5 短名例外

- 在极小作用域内可用习惯短名：仅限 `for/while`
  的索引变量或约 5–10 行内的临时值。
- 允许短名清单（建议严格执行）：`i/j/k`（索引）、`n`（计数）；其他一律使用有含义的名字。
- 作用域一旦扩大（跨多个分支/循环、跨函数、跨文件），必须改为有含义的名字。

### 5.6 集合与复数命名（Collections）

- **数组/列表/可迭代集合**使用复数名词的 `snake_case`：`users`、`order_items`。
- 若复数形式不直观或为不可数名词，使用后缀明确类型：`news_list`、`price_items`。
- **映射/字典（key→value）**使用 `snake_case` 并加后缀 `_map`；必要时可用
  `_by_<key>` 表达键语义（仍需保留 `_map`）：`user_map`、`price_by_symbol_map`。
- **集合/去重集合**使用后缀 `_set`：`user_id_set`、`symbol_set`。

## 6. 常量命名（Constant Names）

- **模块级/全局级固定常量**（写死、与入参无关、加载后不变）使用
  `kPascalCase`，以 `k` 开头。
- 示例：`kDaysInAWeek`、`kAndroid8_0_0`。
- 常量名建议带单位（尤其时间/金额/比例）：例如
  `kTimeoutMs`、`kSpreadBp`、`kRatioPct`。
- 对于**局部 const 但值来自参数/运行时**的变量：
  - 可用普通变量名 `snake_case`；
  - 不要用 `k` 前缀误导读者认为其全局固定。

### 6.1 单元枚举模拟（Unit Enumerations）

TSL 没有内置 `enum`，推荐使用 `unit` + `const` 在 `interface` 区域模拟枚举集合。

- `unit` 名称使用 `PascalCase`，建议以 `Enumerations` 结尾表达用途（例如
  `DocxEnumerations`）。
- 枚举值使用 `const` 定义并放在 `interface` 中：
  - 项目自定义枚举值：默认使用 `kPascalCase`（属于模块级固定常量）。
  - 外部/互操作枚举值：允许沿用对方既有前缀与命名（例外场景）。

示例：

代码块身份：配置片段 / 概念骨架

```tsl
unit AlertEnumerations;
interface

    const kAlertLevelAll = -1;
    const kAlertLevelNone = 0;
end.
```

代码块身份：配置片段 / 概念骨架

```tsl
unit DocxEnumerations;
interface

    // WdAlertLevel
    const wdAlertsAll = -1;
end.
```

## 7. 函数与方法命名（Function Names）

- 顶层函数与“动作型/业务型”方法使用 `PascalCase`。
- 访问器/设置器方法（仅当 property 无法表达语义时才使用）允许使用 `snake_case`
  的小写形式，以贴近“字段/状态”的语义（按团队约定的例外）。
- **特殊函数/运算符重载为语法固定名，必须使用全小写**：
  - 构造/初始化函数：`create`。
  - 析构/释放函数：`destroy`。
  - 运算符重载：`operator+()` 等，按语法使用小写 `operator<op>()` 形式。
- 示例：`AddTableEntry()`、`DeleteUrl()`、`OpenFileOrDie()`。
- **推荐使用 property 语法**对外暴露访问器：property 名
  `PascalCase`，`read/write` 绑定成员变量（见类成员章节）。
- 不推荐新增显式 getter/setter；仅当 property 无法表达语义时，才使用 getter/setter：
  - getter：与字段同形的 `snake_case`（如 `count()`、`is_ready()`）。
  - setter：使用 `set_` 前缀的 `snake_case`（如
    `set_count(x)`、`set_ready(x)`）。

## 8. 宏与编译期开关（Macro Names）

- 能不用宏就不用。
- 若项目不支持宏/预处理，本节可忽略；若支持且必须使用，命名为全大写加下划线，并带项目/业务前缀：
  - `<PROJECT>_ENABLE_FOO`、`<PROJECT>_USE_BAR`、`<PROJECT>_ROUND(x)`。

## 9. 例外（Exceptions）

- 当命名需要与外部既有 API/协议保持一致时，可沿用对方风格（例如对接 C/C++ 库、历史接口、跨语言互操作代码等）。
- 不要为了“命名隔离”引入不必要的 wrapper/嵌套；优先保证语义清晰、可检索，并在必要处用注释说明“该命名来自外部约束/协议”。
