类型值(Type Values)
类型值是 JavaScript 可能值的集合的符号表示——它不像具体值 42 或 "hello" 那样只持有一个值,而是表示共享某些特征的所有值(如「任意数字」或「字面量 1」)。
类型系统是 Abs——{ shape, term?, pred?, conf }——而且它是唯一的类型系统:一个可计算的值,其约束参与代数(x > 0 ⇒ x + 1 > 1)。分析、展示与投影(.d.ts / zod / guard)全部直接消费 Abs;不存在独立的 IR,也没有有损桥接。
四个组成
- shape——外延载体:值长什么样。种类:
prim(带litterm 即精确值)、obj、arr、tuple、fn、eff(promise<…>/generator<…>)、brand(名义实例)、sum(联合)、never、unknown/any。 - term——抽象值身份:
lit(具体值)、var(符号 α,如A1)或app(应用表达式,如(x + 2))。 - pred——相对 term 的约束:
(x + 2) > 3。 - conf——抽象的精确度:
exact/path/widened/mock/partial/opaque。
构造器(num()、strLit(…)、obj({…})…)与核心函数(leqAbs、formatAbs、checkSource…)见 core API。
字面量
带 lit term 的 prim shape 表示恰好一个具体值——引擎从代码字面量或具体 @nudo:case 实参产出它:
25 #exact // 精确的数字 25
"localhost" #exact // 一个特定字符串
基本类型
不带 lit term 的 prim shape 是整个域——知道值属于该类型但不知道具体是哪个:
number // 任意数字
string // 任意字符串
boolean // true 或 false
对象、数组、元组
obj 携带已知槽位(每键 { value, optional? }),arr 单一元素类型,tuple 定长逐元素:
{ host: "localhost", port: 8080, debug: false }
[2, 4, 6] // 字面量元组——元素抽象时为 arr
number[] // 抽象元素
函数与 Promise
fn 携带参数名(或 paramTypes/returnType 签名);eff 包装异步效应,小写渲染:
load: (id) => ? // 函数值,返回未知
promise<{ id: 7, name: "u7" }> // 异步结果
联合
sum 是成员 Abs 的联合——值可能是任一成员:
25 | 9 // 两个精确数字(来自两个 调用点)
number | string // 异构联合
never 是空集(不可达);unknown 是全集。
指令中的类型表达式
@nudo:case / @nudo:mock / @nudo:refine 的实参用约束表达式文法书写——与 *.nudo.js 模板相同的构建器:
| 表达式 | 含义 | 示例 |
|---|---|---|
number() / string() / boolean() | 基本类型域 | @nudo:case "symbolic" (number()) |
lit(v) | 字面量域 | lit(42) / lit("ada") / lit(true) |
union(…) | 成员联合 | union(lit(1), lit(2)) |
shape({ … }) | 对象形状(字段递归) | shape({ id: number().gt(0) }) |
array(…) / record(…) | 数组 / 记录域 | array(number()) |
fn({ … }, …) | 函数关系 | fn({ x: number().gt(0) }, number()) |
| 构建器链 | .gt/.gte/.lt/.lte/.shift/.int… | number().gt(0).int() |
| 裸字面量 | 直接解析 | 42、"abc"、true、[1, 2] |
指令类型表达式使用上面的约束构建器加具体字 面量。旧的 T.* 产品文法已移除。@nudo:mock body 内写普通 JavaScript 值和闭包,不要把构建器调用当作返回负载。
/**
* @nudo:case "concrete" (5, 3)
* @nudo:case "symbolic" (number(), number())
* @nudo:case "mixed" (lit(0), string())
*/
function combine(a, b) {
return a + b;
}
设计原则
Nudo 的类型值体系遵循四条核心原则,决定运算与推断的行为。
1. 字面量保持
所有输入都是字面量时,输出也是字面量。引擎计算出具体结果。
combine(5, 3) // → 8 #exact,不是 number
"ab" + "c" // → "abc" #exact
信息足够时推断类型保持精确。
2. 抽象时拓宽
任一输入抽象(非字面量)时,结果拓宽到相应域——但 Nudo 尽可能保留结构。
1 + number // → number #path(展示为域)
"xy" + string // → string #path(展示为域)
string + string // → string(无结构可保留)
字符串拼接涉及至少一个字面量时,Nudo 内部追踪模板字符串——已知前后缀被保留,这正是 ("user-" + x).startsWith("user-") 在符号 x 下也能折叠为 true #exact 的原因。
3. 惰性联合分布
联合按原样传播。符号值上的运算保持符号——不会急切展开成成员笛卡尔积。这避免组合爆炸并保持相关性:
function selfAdd(a) {
return a + a; // intension: (A1 + A1)——一个符号变量,不是 A1 + A1'
}
selfAdd(1); // → 2 #exact(逐调用点)
selfAdd(2); // → 4 #exact
// Observed: 2 | 4——相关性保持,绝不会是 1+1 | 1+2 | 2+1 | 2+2
抽象实参下结果拓宽到代数判定的域(sum(number, string) → string #path;selfAdd(number) → number #widened)——只有运算符或方法必须区分成员时才逐成员展开。
4. 守卫窄化
类型守卫在分支中窄化值。检查 typeof x === "string" 或 x === null 时,引擎在 if 分支窄化 x,在 else 分支排除这些值。
function process(x) {
if (typeof x === "string") {
// 这里 x 是 string
return x.length; // → number
}
if (x === null) {
// 这里 x 是 null
return 0;
}
// x 已窄化(如输入为 string | number | null 时是 number)
return x;
}
窄化规则支持 typeof、===、!==、instanceof、Array.isArray 与真值检查。