初识 Jvav。
从熟悉的函数和值开始,预览一次计算,再把结果应用到状态。
Jvav 仍在设计中。以下示例依据当前的规范草案编写。
函数与值
函数声明自己的参数与返回类型。这里,twice 接收一个整数,返回这个值与自身的和。
pure fun twice(x: Int): Int {
return x + x
}
val result = twice(21)pure 声明计算的结果来自其声明的依赖,并且不产生外部可观察的副作用。仅仅返回一个值,并不意味着函数是纯函数。
Jvav 保留了熟悉的控制流,包括条件、循环与提前返回。这些语法本身并不意味着会产生外部副作用。
可以预览的计算
transition 描述一次针对状态的计算,结果可以在应用前预览。纯函数可以算出这个结果,以 transition T 返回,而不修改原来的状态。
pure fun swap(
pair: transition Pair
): transition Pair {
return {
a = pair.b
b = pair.a
}
}输入提供特定 pair 的旧状态。两次读取都指向这个状态:描述新的 a,不会改变 pair.a 读到的值。
结果描述 a 与 b 的替换值。在有效应用时,未列出的字段保留原值。计算或返回这个结果,都不会修改 pair。
应用变化
调用方决定何时应用计算所得的变化。在具备必要的修改权限、且目标有效的前提下,草案采用以下写法:
val change = swap(pair)
transition(pair) = change
a = 2, b = 7a = 7, b = 2第一行计算一个值,第二行执行一个副作用。将两者分开,同一套决策逻辑就可以用于检查、模拟与执行。
已保存的 transition 表示已经完成的计算;稍后应用它时,不会重新执行计算。如果目标在此期间发生了变化,应如何处理,草案中仍标记为待定(Pending)。
查看移动示例访问与依赖
参数类型让访问权限变得明确。view T 提供只读、不拥有所有权的访问。mut T 授予修改现有状态的权限,并受函数的副作用契约约束。
context T 声明可由调用环境提供的有类型依赖。伤害计算可以读取实体、查询战斗规则,而不改变世界:
pure fun resolve(
damage: Damage,
target: view Entity,
context: context CombatContext
): DamageResult不同的战斗规则可能产生不同的结果。即使实参由环境隐式提供,上下文也是真实的依赖,相应的访问与副作用限制仍然适用。
表示透明性
无论值来自存储,还是来自跨越调用边界的计算,普通 Vec3 参数的含义都相同。
pure fun lengthSquared(v: Vec3): Float {
return v.x * v.x +
v.y * v.y +
v.z * v.z
}
lengthSquared(a + b)当分量运算满足相应的纯计算条件时,实现可以将加法与后续计算合并,而不构造完整的中间向量。它必须保持所要求的数值结果、副作用与其他可观察行为。
这种表示选择并不意味着普通值采用惰性求值。显式的运行时延迟计算使用 defer T,属于设计中的另一部分。
继续阅读草案。
在草案中查阅精确定义、求值规则与更多示例。所有权、捕获、transition 有效性等仍需作出设计决策的问题,标记为待定(Pending)。