@calcit/procs 0.10.2 → 0.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,109 @@
1
+ # Calcit 局部变量类型标记开发计划
2
+
3
+ > 本计划文档聚焦交付节奏、风险与任务拆解;功能背景与语法示例请参见 `drafts/generic-types.md`。
4
+
5
+ ## 已完成
6
+
7
+ ### 阶段性成果(截至 2026-01-08)
8
+
9
+ - ✅ **阶段 1:数据结构准备** — `CalcitLocal`/`CalcitFn` 结构已扩展,`cargo test` 全量通过。
10
+ - ✅ **阶段 2:语法解析** — `assert-type`、`hint-fn` 全量识别并带单元测试回归。
11
+ - ✅ **阶段 3:类型传播** — `ScopeTypes` 作用域链稳定,`calcit/test-types.cirru` 可复现类型标注。
12
+ - 🚧 **阶段 4:Record/Enum 静态校验** — Record 字段检查已上线,Enum/推断仍在推进。
13
+
14
+ ### 现有实现能力摘要
15
+
16
+ 1. 变量与函数元数据:`CalcitLocal.type_info`、`CalcitFn.return_type/arg_types` 会输出到 IR,供 CLI/工具链消费。
17
+ 2. 语法落地:`assert-type` 在预处理阶段注入类型且运行时代价为 `Nil`;`hint-fn $ return-type ...` 由 `defn` 解析并写入 `CalcitFn.return_type`,同时兼容 `hint-fn async`。
18
+ 3. Record 校验:`&record:get` 与 `user.-field` 调用若访问未声明字段会抛出 `LocatedWarning`,并附上可用字段列表。
19
+ 4. 告警与演示:`calcit/test-types.cirru`、`js-out/program-ir.cirru` 中可直接观察 `:type-info`/`return-type` 字段。
20
+
21
+ ### 测试与示例覆盖
22
+
23
+ - 单元测试:共 11 个,覆盖语法解析、作用域传播、Record 字段合法/非法访问等路径。
24
+ - Cirru 示例:`calcit/test-types.cirru` 展示多种类型标注及 return-type 提示。
25
+ - 典型 Record 告警样例:
26
+
27
+ ```rust
28
+ let test_record = Calcit::Record(CalcitRecord {
29
+ name: EdnTag::from("Person"),
30
+ fields: Arc::new(vec![EdnTag::from("age"), EdnTag::from("name")]),
31
+ values: Arc::new(vec![Calcit::Nil, Calcit::Nil]),
32
+ class: None,
33
+ });
34
+ scope_types.insert(Arc::from("user"), Arc::new(test_record));
35
+ ```
36
+
37
+ > 当代码调用 `(&record:get user :email)` 或 `user.-email` 时,即会产生 `Field 'email' does not exist in record 'Person'` 的预处理警告。
38
+
39
+ ### 已完成任务(Task 4.1~4.5)
40
+
41
+ - 4.1:完成 `defrecord` 管线梳理与字段读取验证 demo。
42
+ - 4.2:在 `program` 层实现 `lookup_record_fields` 类接口,用于 `ScopeTypes` 填充。
43
+ - 4.3:`preprocess_list_call`/`Method(Access)` 均可触发字段验证并输出 `LocatedWarning`。
44
+ - 4.4:补齐 `validates_*field_access` 系列测试与 Cirru 案例。
45
+ - 4.5:`cr --check-only` 能在 demo 中捕获故意写错的字段,验证静态告警链路。
46
+
47
+ ## 未完成
48
+
49
+ ### 风险与考量
50
+
51
+ - **宏展开顺序**:需确保在宏完全展开后仍能关联类型信息,避免遗漏 `assert-type`/`hint-fn`。
52
+ - **性能开销**:预处理阶段的哈希查找可能导致编译变慢,需要关注大型项目的影响。
53
+ - **复杂类型**:当前仅覆盖基础类型与 Record;泛型/联合类型暂未纳入,需规划扩展路线。
54
+
55
+ ### 阶段性开发计划(展望)
56
+
57
+ | 阶段 | 目标 | 交付物 |
58
+ | :----------------------- | :---------------------------------------------------------------------------- | :---------------------------------------- |
59
+ | 阶段 1:数据结构准备 | 扩展 `CalcitLocal` 与相关派生,实现 `type_info` 字段并占位 `assert-type` 语法 | 可编译版本 + 基础单测 |
60
+ | 阶段 2:语法解析 | Cirru→Calcit 过程识别 `assert-type`、`hint-fn` 并保留 AST 注解 | `cr --check-only` 示例 |
61
+ | 阶段 3:类型传播 | `preprocess_*` 传递 `ScopeTypes` 并在 `Local` 中带上 `type_info` | demo 函数 + `&inspect-type` helper |
62
+ | 阶段 4:Record 静态校验 | 校验 `Record` 字段、提供错误定位、覆盖 `&record:get`/`.-field` | 合法/非法调用示例 + `cr query error` 输出 |
63
+ | 阶段 5:运行时与内置函数 | `&inspect-type` 原语、内置函数签名、`unknown` 回退策略 | 运行时示例 + 文档/测试 |
64
+
65
+ > 阶段 1~3 已交付;表格保留原路线,方便追踪阶段 4/5 的补完情况。
66
+
67
+ ### 阶段 4 剩余工作
68
+
69
+ 1. **Record 字面量推断**:识别 `&new-record`/`&%{}` 构造,自动写入 `ScopeTypes`。
70
+ 2. **Enum 变体验证**:校验 `tag-match`/`defenum` 变体名称与参数个数。
71
+ 3. **`hint-fn` 元信息消费**:
72
+ - 批量补齐 `return-type` 注解(核心库以外)。
73
+ - 在 `program-ir`/`cr query` 中展示返回值期望,为调用点校验铺路。
74
+
75
+ ### 待办任务清单(按主题拆分)
76
+
77
+ #### Enum variant 支持(可选)
78
+
79
+ - [ ] **任务 4.6**:调研/确认 `defenum` 现状,确定是实现新语法还是复用 Record。
80
+ - [ ] **任务 4.7**:为 `tag-match`/`case` 实现变体验证并输出 `LocatedWarning`。
81
+
82
+ #### 内置函数类型提示
83
+
84
+ - [ ] **任务 4.8**:设计 `CalcitProc` 签名描述结构(字符串或 Calcit 值)。
85
+ - [ ] **任务 4.9**:为常用 proc(`+`、`map`、`assoc` 等)补充签名并加单测。
86
+ - [ ] **任务 4.10**:预处理阶段读取签名,对参数数量/类型做最小校验。
87
+
88
+ #### 运行时工具与诊断
89
+
90
+ - [ ] **任务 4.11**:实现 `&inspect-type` 原语,便于 REPL 观察类型标注。
91
+ - [ ] **任务 4.12**:在 `LocatedWarning::print_list` 等路径输出变量 `type_info`,提升可调试性。
92
+
93
+ #### 文档与示例
94
+
95
+ - [ ] **任务 4.13**:撰写 `docs/type-annotations.md`,汇总语法与 Record 校验示例。
96
+ - [ ] **任务 4.14**:更新 `README`/changelog,记录类型标记能力上线情况。
97
+
98
+ #### 集成测试与性能
99
+
100
+ - [ ] **任务 4.15**:运行全量测试,观察大项目中的预处理性能。
101
+ - [ ] **任务 4.16**:必要时对 `ScopeTypes` 克隆等热路径做 profiling/优化(例如引入 `Arc` 共享)。
102
+
103
+ ### 执行顺序建议
104
+
105
+ 1. **阶段 4 主线**:Record 字面量推断 → Enum 变体验证 → `hint-fn` 元信息消费。
106
+ 2. **配套能力**:运行时工具(4.11/4.12)与内置函数签名(4.8~4.10)可交叉进行。
107
+ 3. **收尾**:文档/示例(4.13/4.14),随后执行性能与集成测试(4.15/4.16)。
108
+
109
+ _评估日期:2026-01-08 · 当前状态:阶段 3 已完成,阶段 4 进行中_
@@ -0,0 +1,528 @@
1
+ # Calcit 局部变量类型标记方案评估
2
+
3
+ 本文档评估在 Calcit 中为 `Local` 变量补充类型信息的技术方案及工作量。涉及任务进度与交付计划请参阅 `drafts/generic-types-plan.md`。
4
+
5
+ ## 1. 核心目标
6
+
7
+ - 在宏展开后的 IR (Intermediate Representation) 中,为 `Local` 变量关联类型信息。
8
+ - 支持 `assert-type` 和 `hint-fn` 语法进行函数参数、返回值类型标记。
9
+ - 自动利用 `Record` 信息进行方法调用(Method Call)的静态验证。
10
+ - 允许 `unknown` 类型,并提供运行时查看手段。
11
+ - 为内置函数补充类型提示,保持向后兼容。
12
+
13
+ ## 2. 技术方案建议
14
+
15
+ ### 2.0 类型声明语法设计
16
+
17
+ Calcit 以前没有预置函数类型写法,现通过 `assert-type` 和 `hint-fn` 来声明类型信息:
18
+
19
+ #### 2.0.1 基本类型声明
20
+
21
+ ```cirru
22
+ defn f1 (x y)
23
+ assert-type x :number ; 声明参数 x 为 number 类型
24
+ assert-type y $ :fn (:number) :number ; 声明参数 y 为函数类型
25
+ hint-fn $ return-type :number ; 声明返回值类型
26
+ &+ x (y 10)
27
+ ```
28
+
29
+ #### 2.0.2 类型表示方式
30
+
31
+ - **内置类型**:使用 Tag 直接表示
32
+
33
+ - `:number` - 数字类型
34
+ - `:string` - 字符串类型
35
+ - `:bool` - 布尔类型
36
+ - `:nil` - nil 类型
37
+ - `:tag` - 标签类型
38
+ - `:fn` - 函数类型(基础形式,用户定义的函数)
39
+ - `:fn (arg-types...) return-type` - 函数类型(带签名)
40
+ - `:proc` - Proc 类型(内置函数,由 Rust/JavaScript 实现)
41
+ - `:proc (arg-types...) return-type` - Proc 类型(带签名)
42
+
43
+ - **自定义类型**:使用 Record 定义来表示
44
+
45
+ - `:User` - 当前 namespace 中定义的 Record
46
+ - 类型信息通过 `defrecord` 关联的元数据获取
47
+
48
+ - **复合类型**(仅用于声明,不做推断):
49
+ - `:list :number` - 元素为 number 的 list(需手动标注)
50
+ - `:map :tag :string` - key 为 tag,value 为 string 的 map(需手动标注)
51
+ - **注意**:Calcit 不会自动推断复合类型的内部元素类型,所有复合类型信息必须通过 `assert-type` 显式声明
52
+
53
+ #### 2.0.3 `assert-type` 语法
54
+
55
+ `assert-type` 用于在函数体内声明变量类型:
56
+
57
+ ```cirru
58
+ assert-type <variable> <type-expr>
59
+ ```
60
+
61
+ - `<variable>`:要标注类型的变量名(Symbol)
62
+ - `<type-expr>`:类型表达式,可以是 Tag 或嵌套的列表结构
63
+ - **返回值**:`assert-type` 返回变量本身的值(不做改变),这使得它可以在表达式中内联使用,如 `(assert-type x :number)`
64
+
65
+ **关键特性**:
66
+
67
+ - `assert-type a :string` 在 preprocess 阶段将类型信息注入到作用域,并在运行时返回 `a` 的值不变
68
+ - 可以在串联调用(如 `->` 宏)中使用,保持表达式流畅性
69
+ - 返回值保留原始值,使得类型断言透明化
70
+
71
+ 示例:
72
+
73
+ ```cirru
74
+ defn process-user (user)
75
+ assert-type user :User ; user 是 User Record 类型
76
+ get user :name ; 可以静态检查字段是否存在
77
+
78
+ defn map-numbers (f xs)
79
+ assert-type f $ :fn (:number) :number ; f 是函数类型(用户定义)
80
+ assert-type xs $ :list :number ; xs 是 number 列表
81
+ map f xs
82
+
83
+ defn use-builtin-proc (p x)
84
+ assert-type p :proc ; p 是内置函数(Proc)
85
+ assert-type x :number
86
+ p x
87
+
88
+ ; Proc 类型签名示例:
89
+ defn calculate (a b)
90
+ assert-type a :number
91
+ assert-type b :number
92
+ ; &+ 有内置类型签名: (number, number) -> number
93
+ let
94
+ sum $ &+ a b ; sum 自动推断为 :number
95
+ ; floor 有内置类型签名: number -> number
96
+ floor $ &/ sum 2.0 ; 结果为 :number
97
+ ```
98
+
99
+ #### 2.0.4 `hint-fn` 语法
100
+
101
+ `hint-fn` 用于在函数体内声明函数级别的元信息(如返回值类型):
102
+
103
+ ```cirru
104
+ hint-fn $ return-type <type-expr>
105
+ ```
106
+
107
+ 示例:
108
+
109
+ ```cirru
110
+ defn add-numbers (a b)
111
+ assert-type a :number
112
+ assert-type b :number
113
+ hint-fn $ return-type :number
114
+ &+ a b
115
+ ```
116
+
117
+ #### 2.0.5 类型传播机制
118
+
119
+ 1. `assert-type` 将类型信息注入到 `ScopeTypes` 映射中
120
+ 2. 预处理器在转换 `Symbol` → `Local` 时查询并填充 `type_info` 字段
121
+ 3. 类型信息在作用域内传播,后续对同一变量的引用保留类型标注
122
+ 4. `hint-fn` 的返回值类型信息存储在函数定义的元数据中
123
+
124
+ #### 2.0.6 未标注类型的处理
125
+
126
+ - 默认所有未标注的变量 `type_info` 为 `None`,对应 `unknown` 类型
127
+ - `unknown` 类型不触发静态类型检查
128
+ - 保持向后兼容:老代码无需修改即可运行
129
+
130
+ #### 2.0.7 类型系统定位与范围
131
+
132
+ **重要说明**:Calcit 的类型系统是一个**轻量级类型检测机制**,专为 Calcit 语言的特定需求设计,具有以下定位:
133
+
134
+ 1. **显式标注,不做推断**:
135
+
136
+ - 类型信息通过 `assert-type` 和 `hint-fn` **手动声明**
137
+ - **不实现类型推断**(Type Inference):不会自动分析代码推导变量类型
138
+ - 设计目标是为 preprocess 阶段提供辅助信息,而非构建完整的类型系统
139
+
140
+ 2. **辅助性质,非强制约束**:
141
+
142
+ - 主要用于在编译/预处理阶段提供**早期错误检测**(如 Record 字段访问验证)
143
+ - 类型信息用于优化代码生成和方法调用解析
144
+ - 不影响运行时行为(除非显式添加运行时检查)
145
+
146
+ 3. **Calcit 语言专属**:
147
+
148
+ - 针对 Calcit 的 Record、Tuple、动态方法调用等特性定制
149
+ - 不追求通用类型系统的完整性(如 Haskell/OCaml 风格)
150
+ - 优先保证与现有 Calcit 代码的兼容性
151
+
152
+ 4. **Proc (内置函数) 类型检查支持**:
153
+
154
+ - Calcit 的内置函数(Proc)现在也支持类型签名
155
+ - 通过 `CalcitProc::get_type_signature()` 方法提供类型信息
156
+ - 在预处理阶段自动检查 Proc 调用的参数类型
157
+ - 当传递类型不匹配的参数时,生成编译期警告
158
+
159
+ 示例:
160
+
161
+ ```cirru
162
+ defn test-math ()
163
+ let
164
+ x 10
165
+ text |hello
166
+ assert-type x :number
167
+ assert-type text :string
168
+
169
+ ; 正确:传递 number 类型给 &+
170
+ &+ x 20 ; ✓ 通过类型检查
171
+
172
+ ; 错误:传递 string 类型给 &+(需要 number)
173
+ &+ text 10 ; ✗ 生成警告: Proc `&+` arg 1 expects type `:number`, but got `:string`
174
+ ```
175
+
176
+ 5. **渐进式采用**:
177
+ - 可选功能:无需为所有代码添加类型标注
178
+ - 新代码可以逐步引入类型标注,老代码继续正常运行
179
+ - 适用于大型项目的局部重构和增强
180
+
181
+ **典型使用场景**:
182
+
183
+ - 为关键函数添加类型标注,捕获常见错误
184
+ - 在复杂的数据处理流程中验证类型正确性
185
+ - 配合 Record 使用,提供静态字段检查
186
+ - 在 -> 等串联语法中保持类型信息流动
187
+ - **Proc 类型签名**:内置函数(如 `&+`、`floor`)已预定义类型签名,可自动参与类型检查
188
+
189
+ **非目标**:
190
+
191
+ - 完整的类型推断引擎(不实现 Hindley-Milner 等算法)
192
+ - 覆盖所有 Calcit 表达式的类型验证
193
+ - 与其他静态类型语言的互操作性
194
+
195
+ #### 2.0.8 Proc 类型签名系统
196
+
197
+ 为增强内置函数的类型安全性,Calcit 为 `CalcitProc`(内置函数)实现了类型签名系统。
198
+
199
+ **实现机制**:
200
+
201
+ 在 `src/calcit/proc_name.rs` 中定义:
202
+
203
+ ```rust
204
+ pub struct ProcTypeSignature {
205
+ pub return_type: Option<Arc<Calcit>>,
206
+ pub arg_types: Vec<Option<Arc<Calcit>>>,
207
+ }
208
+
209
+ impl CalcitProc {
210
+ pub fn get_type_signature(&self) -> Option<ProcTypeSignature>;
211
+ pub fn has_type_signature(&self) -> bool;
212
+ }
213
+ ```
214
+
215
+ **已支持的 Proc**:
216
+
217
+ **Meta 操作**:
218
+
219
+ - `type-of` → `any -> tag`
220
+ - `format-to-lisp`, `format-to-cirru` → `any -> string`
221
+ - `turn-symbol` → `string -> symbol`
222
+ - `turn-tag` → `string -> tag`
223
+ - `&compare` → `(any, any) -> number`
224
+ - `&get-os` → `() -> tag`
225
+ - `&hash` → `any -> number`
226
+
227
+ **数学运算**:
228
+
229
+ - `&+`, `&-`, `&*`, `&/`, `pow`, `&number:rem` → `(number, number) -> number`
230
+ - `floor`, `ceil`, `round`, `sin`, `cos`, `sqrt`, `&number:fract` → `number -> number`
231
+ - `round?` → `number -> bool`
232
+ - `bit-shl`, `bit-shr`, `bit-and`, `bit-or`, `bit-xor` → `(number, number) -> number`
233
+ - `bit-not` → `number -> number`
234
+
235
+ **比较和逻辑**:
236
+
237
+ - `&=`, `&<`, `&>`, `identical?` → `(any, any) -> bool`
238
+ - `not` → `bool -> bool`
239
+
240
+ **字符串操作**(40+ 个):
241
+
242
+ - `&str:concat` → `(string, string) -> string`
243
+ - `trim`, `turn-string` → `any -> string`
244
+ - `&str` → `(...any) -> string` (variadic)
245
+ - `split`, `split-lines` → `string -> list`
246
+ - `starts-with?`, `ends-with?` → `(string, string) -> bool`
247
+ - `get-char-code` → `string -> number`
248
+ - `char-from-code` → `number -> string`
249
+ - `pr-str` → `any -> string`
250
+ - `parse-float` → `string -> number`
251
+ - `blank?` → `string -> bool`
252
+ - `&str:replace` → `(string, string, string) -> string`
253
+ - `&str:slice` → `(string, number, number) -> string`
254
+ - `&str:find-index` → `(string, string) -> number`
255
+ - `&str:escape`, `&str:first`, `&str:nth` → `string -> string`
256
+ - `&str:rest` → `string -> string`
257
+ - `&str:count` → `string -> number`
258
+ - `&str:empty?`, `&str:contains?`, `&str:includes?` → `string -> bool`
259
+ - `&str:pad-left`, `&str:pad-right` → `(string, number, string) -> string`
260
+
261
+ **列表操作**(25+ 个):
262
+
263
+ - `[]` → `(...any) -> list`
264
+ - `append`, `prepend` → `(list, any) -> list`
265
+ - `butlast`, `&list:reverse`, `&list:distinct` → `list -> list`
266
+ - `range` → `number -> list`
267
+ - `sort` → `list -> list`
268
+ - `&list:concat` → `(list, list) -> list`
269
+ - `&list:count` → `list -> number`
270
+ - `&list:empty?`, `&list:contains?`, `&list:includes?` → `list -> bool`
271
+ - `&list:slice` → `(list, number, number) -> list`
272
+ - `&list:nth`, `&list:first` → `list -> any`
273
+ - `&list:rest` → `list -> list`
274
+ - `&list:assoc`, `&list:assoc-before`, `&list:assoc-after` → `(list, number, any) -> list`
275
+ - `&list:dissoc` → `(list, number) -> list`
276
+ - `&list:to-set` → `list -> set`
277
+
278
+ **Map 操作**(15+ 个):
279
+
280
+ - `&{}` → `(...any) -> map`
281
+ - `&merge`, `&merge-non-nil` → `(map, map) -> map`
282
+ - `to-pairs`, `&map:to-list` → `map -> list`
283
+ - `&map:get` → `(map, any) -> any`
284
+ - `&map:dissoc` → `(map, any) -> map`
285
+ - `&map:count` → `map -> number`
286
+ - `&map:empty?`, `&map:contains?`, `&map:includes?` → `map -> bool`
287
+ - `&map:assoc` → `(map, any, any) -> map`
288
+ - `&map:diff-new`, `&map:diff-keys`, `&map:common-keys` → `(map, map) -> set`
289
+
290
+ **Set 操作**(10+ 个):
291
+
292
+ - `#{}` → `(...any) -> set`
293
+ - `&include`, `&exclude` → `(set, any) -> set`
294
+ - `&difference`, `&union`, `&set:intersection` → `(set, set) -> set`
295
+ - `&set:to-list` → `set -> list`
296
+ - `&set:count` → `set -> number`
297
+ - `&set:empty?`, `&set:includes?` → `set -> bool`
298
+
299
+ **Tuple 操作**:
300
+
301
+ - `::` → `(...any) -> tuple`
302
+ - `&tuple:nth` → `(tuple, number) -> any`
303
+ - `&tuple:assoc` → `(tuple, number, any) -> tuple`
304
+ - `&tuple:count` → `tuple -> number`
305
+
306
+ **Record 操作**:
307
+
308
+ - `&%{}` → `(tag, ...) -> record`
309
+ - `&record:with`, `&record:assoc` → `(record, any, any) -> record`
310
+ - `&record:get` → `(record, tag) -> any`
311
+ - `&record:count` → `record -> number`
312
+ - `&record:contains?`, `&record:matches?` → `record -> bool`
313
+ - `&record:to-map` → `record -> map`
314
+ - `&record:from-map` → `(record, map) -> record`
315
+ - `&record:get-name` → `record -> tag`
316
+
317
+ **I/O 和环境**:
318
+
319
+ - `read-file` → `string -> string`
320
+ - `write-file` → `(string, string) -> nil`
321
+ - `get-env` → `string -> string`
322
+ - `cpu-time` → `() -> number`
323
+
324
+ **Refs/Atoms**:
325
+
326
+ - `atom` → `any -> ref`
327
+ - `&atom:deref` → `ref -> any`
328
+
329
+ **Cirru 格式**:
330
+
331
+ - `parse-cirru`, `parse-cirru-edn` → `string -> any`
332
+ - `format-cirru`, `format-cirru-edn` → `any -> string`
333
+
334
+ **总计**:已为 **150+ 个 Proc** 添加类型签名,覆盖:
335
+
336
+ - ✅ 所有数学运算
337
+ - ✅ 所有字符串操作
338
+ - ✅ 所有集合操作(List, Map, Set, Tuple, Record)
339
+ - ✅ 所有比较和逻辑运算
340
+ - ✅ I/O 和环境操作
341
+ - ✅ 类型转换和检查
342
+
343
+ **未包含签名的 Proc**:主要是特殊控制流(`recur`, `raise`)和一些元编程工具,这些通常需要特殊处理。
344
+
345
+ **使用示例**:
346
+
347
+ ```cirru
348
+ defn calculate (a b)
349
+ assert-type a :number
350
+ assert-type b :number
351
+ ; &+ 有预定义签名 (number, number) -> number
352
+ let
353
+ sum $ &+ a b ; sum 自动推断为 :number
354
+ floor $ &/ sum 2.0 ; 返回 :number
355
+ ```
356
+
357
+ **扩展方式**:在 `CalcitProc::get_type_signature()` 中添加新的匹配分支即可。
358
+
359
+ ### 2.1 数据结构调整
360
+
361
+ #### 2.1.1 `CalcitLocal` 扩展
362
+
363
+ 在 `src/calcit/local.rs` 中修改 `CalcitLocal` 结构体:
364
+
365
+ ```rust
366
+ pub struct CalcitLocal {
367
+ pub idx: u16,
368
+ pub sym: Arc<str>,
369
+ pub info: Arc<CalcitSymbolInfo>,
370
+ pub location: Option<Arc<Vec<u16>>>,
371
+ // 新增:类型信息,可以是 Tag, Record 或其它 Calcit 值
372
+ pub type_info: Option<Arc<Calcit>>,
373
+ }
374
+ ```
375
+
376
+ #### 2.1.2 函数定义元数据扩展
377
+
378
+ 为支持 `hint-fn` 声明的返回值类型,需要在函数相关结构中添加类型元数据字段:
379
+
380
+ ```rust
381
+ // 在函数定义相关结构中添加(具体位置取决于实现)
382
+ pub struct FnInfo {
383
+ // ... 现有字段
384
+ pub return_type: Option<Arc<Calcit>>, // 返回值类型
385
+ pub arg_types: Vec<Option<Arc<Calcit>>>, // 参数类型列表
386
+ }
387
+ ```
388
+
389
+ - `return_type`: 通过 `hint-fn $ return-type :type` 声明
390
+ - `arg_types`: 通过 `assert-type arg :type` 在函数体内收集
391
+ - 默认值为 `None`,表示 `unknown` 类型
392
+
393
+ ### 2.2 预处理器增强 (`preprocess.rs`)
394
+
395
+ 预处理器是类型信息注入的关键位置:
396
+
397
+ 1. **作用域跟踪**:`preprocess_expr` 需要传递一个类型映射表 `ScopeTypes = HashMap<Arc<str>, Arc<Calcit>>`。
398
+
399
+ 2. **`assert-type` 处理**:
400
+
401
+ - 识别 `assert-type var type` 语法节点
402
+ - 解析 `var` 对应的变量名,将 `type` 表达式求值并记录到 `ScopeTypes` 映射中
403
+ - `assert-type` 在预处理后可以作为 No-op 或保留作为运行时断言
404
+
405
+ 3. **`hint-fn` 处理**:
406
+
407
+ - 识别 `hint-fn $ return-type type` 语法
408
+ - 将返回值类型信息关联到当前函数定义的元数据中
409
+ - 可扩展支持其他函数级别的提示(如 `hint-fn $ pure true`)
410
+
411
+ 4. **Local 生成**:
412
+
413
+ - 当预处理器将 `Symbol` 转换为 `Local` 时,从 `ScopeTypes` 中查询并填充 `type_info`
414
+ - 确保类型信息在作用域内正确传播
415
+
416
+ 5. **方法校验**:
417
+ - 在 `preprocess_list_call` 中,识别 `get`/`.-field` 等方法调用
418
+ - 如果参数是带有 `Record` 类型的 `Local`,根据 Record 定义校验字段合法性
419
+ - 不匹配时生成 `LocatedWarning`
420
+
421
+ ### 2.3 内置函数与类型提示
422
+
423
+ - **Procs 注册**:在 `src/builtins.rs` 中,需要一种方式为 `CalcitProc` 关联签名信息。
424
+ - **已实现**:`CalcitProc::get_type_signature()` 方法返回 `Option<ProcTypeSignature>`
425
+ - `ProcTypeSignature` 包含 `return_type` 和 `arg_types` 字段
426
+ - 已为常用 Proc 添加类型签名:
427
+ - 数学运算:`&+`, `&-`, `&*`, `&/` 等 - `(number, number) -> number`
428
+ - 数学函数:`floor`, `ceil`, `sin`, `cos`, `sqrt` 等 - `number -> number`
429
+ - 比较运算:`&=`, `&<`, `&>` 等 - `(any, any) -> bool`
430
+ - 逻辑运算:`not` - `bool -> bool`
431
+ - 类型检查:`type-of` - `any -> tag`
432
+ - 可以逐步扩展更多 Proc 的类型信息
433
+ - **兼容性**:老代码保持 `type_info` 为 `None` (即 `unknown`),不进行强校验。
434
+
435
+ ### 2.4 运行时支持
436
+
437
+ - 增加 `&inspect-type` 原语,允许在运行时通过 `CalcitLocal` 获取其标记的类型名。
438
+
439
+ ## 3. `defenum` 设计草案
440
+
441
+ 为了在 tuple 上实现更强的代数数据类型能力,可以引入轻量的 `defenum` 语法,为现有的 `tag-match` 提供结构化的类型声明。
442
+
443
+ ### 6.1 现有基础:`tag-match`
444
+
445
+ Calcit 已经具备基于 tagged tuple 的模式匹配能力:
446
+
447
+ ```cirru
448
+ ; 现有用法
449
+ tag-match (:: :ok 1)
450
+ (:ok v) (&+ v 10)
451
+ (:err e) (eprintln e)
452
+
453
+ tag-match (:: :some |hello)
454
+ (:some x) (str |got: x)
455
+ (:none) |nothing
456
+ ```
457
+
458
+ **现状分析**:
459
+
460
+ - ✅ 已有 `::` 语法创建 tagged tuple
461
+ - ✅ 已有 `tag-match` 宏进行 pattern matching
462
+ - ✅ 支持参数数量检查(`tag-match` 会验证 tuple 长度)
463
+ - ❌ 缺少类型声明:无法事先定义合法的 variant 集合
464
+ - ❌ 缺少静态校验:拼写错误的 tag 名称不会被发现
465
+
466
+ ### 6.2 用 `defrecord` 表达枚举(变体用属性数组)
467
+
468
+ 为减少新语法引入的复杂度,使用已有的 `defrecord` 来表示枚举,每个变体作为一个 `Record` 类型,参数改为属性数组进行约束:
469
+
470
+ ```cirru
471
+ ; 一个定义包含所有变体:每个 field 对应一个 enum 的 tag
472
+ defrecord! Result
473
+ :ok $ [] V1
474
+ :err $ [] :string
475
+
476
+ defrecord! Option
477
+ :some $ [] V1
478
+ :none $ []
479
+
480
+ defrecord! Event
481
+ :click $ [] :number :number
482
+ :keypress $ [] :string :bool
483
+ :message $ [] :string VUser :number
484
+
485
+ ; 使用时用 assert-type 标注,并通过属性数组校验字段
486
+ defn handle-result (result)
487
+ assert-type result :Result
488
+ tag-match result
489
+ (:ok v) (println |Success: v)
490
+ (:err e) (println |Error: e)
491
+ ```
492
+
493
+ 说明:上述 `Result` 定义中,`V1` 表示某个具体的 Record 类型(例如用户自定义的 `Record`),而 `:string` 则表示内置的字符串类型标记。
494
+
495
+ 为了承载这些 enum 元数据,tuple 构造函数新增 `%%::` 形式:
496
+
497
+ ```cirru
498
+ %%:: Action Result :ok payload
499
+ ```
500
+
501
+ 它会把 `Action` 作为 class/trait,同时把 `Result` 作为 sum type。对应实现中,`CalcitTuple` 增加了 `sum_type` 字段,以便方法分派(`class`)和 `tag-match` 校验(`sum_type`)可以同时使用这些信息。
502
+
503
+ **要点**:
504
+
505
+ - 变体是现有 `Record`,字段由属性数组限定;
506
+ - 通过 `assert-type` 为变量标注为具体变体或联合(如 `|or` 简写联合);
507
+ - 预处理时利用 `Record` 元数据进行字段存在性校验,避免新增枚举存储结构;
508
+ - 与现有 `record-match`/`&record:get` 检查机制复用,减少实现成本。
509
+
510
+ ## 4. `hint-fn` 返回值标记落地纪要(2026-01-09)
511
+
512
+ 本轮迭代完成了 `hint-fn $ return-type ...` → `CalcitFn.return_type` 的闭环,实现细节与验证路径记录如下。
513
+
514
+ ### 9.1 实现成果
515
+
516
+ - `builtins/syntax::defn` 在展开函数体时会扫描全部 `hint-fn` 语句,遇到 `$ return-type ...` 结构即解析出类型表达式,后一条提示可覆盖前一条,最终写入 `CalcitFn.return_type`。
517
+ - `hint-fn async` 的既有语义保持不变,会被原样保留在函数体中供 JS codegen 检测异步函数。
518
+ - `calcit-core.cirru` 与 `calcit/test-types.cirru` 已补充一批 `hint-fn` 标注来喂数,`cargo run --bin cr -- -1 calcit/test-types.cirru ir` 后可在 `program-ir.cirru` 中看到 `return-type` 字段;同样可以通过 `rg return-type js-out/program-ir.cirru` 快速确认导出的 IR。
519
+ - 新增的 `builtins::syntax`/`program::ir` 单元测试覆盖了解析与序列化流程,确保 `return-type` 元信息在 AST→IR 全链路上都有快照保护。
520
+
521
+ ### 9.2 后续跟进
522
+
523
+ 1. 扩大 `hint-fn` 标注覆盖面:把核心库、常用模块以及示例项目中的函数逐步加上 `return-type`,累计形成可靠的数据基础。
524
+ 2. 将 `return-type` 对外可视化:在 `cr query def`/`cr docs` 或 LSP 返回值里显示该字段,方便 IDE/CLI 读取。
525
+ 3. 设计最小的调用点静态检查,基于 `return-type` 给出不匹配的早期提示(例如数值上下文误用了 `str`)。
526
+ 4. 与 `CalcitFn.arg_types` 聚合,将完整的函数签名输出到 IR,便于后续 tooling(类型浏览器、文档生成器)消费。
527
+
528
+ > 备注:`assert-type` → `CalcitFn.arg_types` 的聚合仍在规划中,本纪要聚焦于 `return-type` 链路;相关构建和 `yarn check-all` 均已通过,确保引入的元信息不会影响现有运行路径。
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.9.20",
3
+ "version": "0.10.5",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "devDependencies": {
6
6
  "@types/node": "^24.1.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.10.2",
3
+ "version": "0.10.5",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "devDependencies": {
6
6
  "@types/node": "^24.1.0",
@@ -1,20 +0,0 @@
1
-
2
- {}
3
- |added $ {}
4
- |changed $ {}
5
- |quaternion.test $ {}
6
- :changed-defs $ {}
7
- |test-add $ quote
8
- deftest test-add
9
- testing "|multiply quaternion" $ is
10
- = (:: :quaternion -60 12 30 24)
11
- &q* (:: :quaternion 1 2 3 4) (:: :quaternion 5 6 7 8)
12
- testing "\"add complex"
13
- is $ = (:: :complex 9 12)
14
- c+ (:: :complex 1 2) (:: :complex 3 4) (:: :complex 5 6)
15
- is $ = (:: :complex 4 6)
16
- c+ (:: :complex 1 2) (:: :complex 3 4)
17
- testing "🚀 Updated demo test case" $ is
18
- = (:: :complex 10 10)
19
- c+ (:: :complex 3 4) (:: :complex 7 6)
20
- |removed $ #{}