Wing 语言 math.log2 实战:从 SDK 实现到 hangar 快照验证

原创2026-10-13 18:41:071,625 阅读
文章标签:编程语言编译器云原生语言运行时

Wing 语言 math.log2 实战:从 SDK 实现到 hangar 快照验证

math.log2 是 Wing 云编程语言标准库 math 模块中用于计算以 2 为底的对数的核心工具函数。本文以仓库中 log2.test.w 测试用例 及其 hangar 快照(tf-aws 编译快照、sim 运行快照)为线索,带你完整掌握 math.log2 的调用方式、边界语义、底层实现,以及 Wing 项目如何通过 hangar 快照机制同时验证 preflight 编译产物与 inflight 运行时行为。读完本文,你将能在自己的 Wing 项目中正确使用对数运算,并理解 Wing SDK 数学函数"一次编写、双阶段验证"的工程机制。

一、测试源文件:preflight 与 inflight 的双重断言

Wing 语言的核心模型是 preflight(编译期基础设施代码)与 inflight(运行期业务代码)两个阶段。位于 tests/sdk_tests/math/log2.test.w 的测试用例完美展示了这一模型:

bring math;

assert(math.log2(3) == 1.584962500721156);
assert(math.log2(2) == 1);
assert(math.log2(1) == 0);
assert(math.log2(0) == -math.INF);

test "inflight absolute" {
  assert(math.log2(3) == 1.584962500721156);
  assert(math.log2(2) == 1);
  assert(math.log2(1) == 0);
  assert(math.log2(0) == -math.INF);
}
  • 文件顶部 bring math; 引入标准库 math 模块,之后即可通过 math.log2(...) 静态调用;
  • 前 4 行 assert 位于 preflight 阶段,在编译/部署基础设施时即被执行,验证数值结果;
  • 用 test "..." { } 包裹的块是 inflight 阶段测试,运行时在模拟器或云端环境中执行,验证同一函数在运行期的行为一致性。

这种"同一断言逻辑在 preflight 与 inflight 各跑一遍"的写法,正是 Wing 测试 sdk_tests 目录下数学类用例的通用模式(同目录下还有 log.test.w、log10.test.w 等 33 个用例可对照)。

二、math.log2 的 API 签名与语义

官方 API 参考文档 docs/api/04-standard-library/math/api-reference.md 对 log2 的定义如下:

bring math;

math.log2(value: num);
要素 说明
所属模块 math(Wing 标准库)
调用形式 math.log2(value: num),静态方法,无需实例化
参数 value 必填,类型 num,即输入数值
返回值 num,输入值以 2 为底的对数
官方描述 "Returns the base-2 logarithm of a number."(返回一个数的以 2 为底的对数)

同一 API 参考中 log 对应自然对数(base e)、log10 对应以 10 为底的对数,三者构成 Wing 数学库的完整对数函数族。

三、底层实现:直接映射 JavaScript Math.log2

math.log2 并非独立重新实现,而是封装了底层 JS 运行时的原生能力。在 packages/@winglang/sdk/src/math/math.ts 中可以看到:

/**
 * Returns the base-2 logarithm of a number.
 * @param value The input number.
 */
public static log2(value: number): number {
  return Math.log2(value);
}

Util 是 math 模块的承载类(API 参考中称为 Math,全限定名 @winglang/sdk.math.Util),所有数学函数均为 public static 方法,因此 Wing 侧使用 math.log2(...) 即可直接调用。与 log2 相邻的 log、log10 同样是一行封装:

public static log(value: number): number {
  return Math.log(value);
}

public static log10(value: number): number {
  return Math.log10(value);
}

这意味着 math.log2 的行为与 ECMAScript 标准中的 Math.log2 完全一致——包括精度、舍入规则和边界值处理,测试用例中的 math.log2(3) == 1.584962500721156 正是这一底层行为的结果。

四、边界语义:math.log2(0) 与 math.INF 常量

测试用例中最值得注意的断言是 assert(math.log2(0) == -math.INF);,它验证了对数函数的负无穷边界:

  • 数学上,log2(0) 趋向于 -∞,JS 运行时返回 -Infinity;
  • Wing 的 math 模块为此提供了正无穷常量 INF,定义于 math.ts:
/**
 * Positive infinity constant
 */
public static readonly INF = Number.POSITIVE_INFINITY;

因此 -math.INF 恰好表达"负无穷"语义,可直接参与数值比较。该常量与 PI、TAU、E(分别对应 Math.PI、2 * Math.PI、Math.E)一起构成 Util 类的静态常量组,供 math.PI、math.TAU、math.E、math.INF 形式访问,同目录下的 euler.test.w、pi.test.w、tau.test.w 即针对这些常量做了专项验证。

五、tf-aws 编译快照:为什么纯计算代码没有云资源

关联文档 log2.test.w_compile_tf-aws.md 是 hangar 对 log2.test.w 执行 wing compile -t tf-aws 后生成的 Terraform JSON 快照,全文如下:

{
  "//": {
    "metadata": {
      "backend": "local",
      "stackName": "root"
    },
    "outputs": {}
  },
  "provider": {
    "aws": [
      {}
    ]
  }
}

这份快照揭示了 Wing 编译模型的一个重要事实:

  • log2.test.w 虽然 bring 了 math 标准库,但没有创建任何云资源(没有 Bucket、Queue、Function 等),因此编译产物 main.tf.json 中没有任何 resource 节点;
  • 整个清单仅保留 provider.aws 的空配置(aws: [{}]),这是 tf-aws 平台为目标平台注入的最小必要声明,backend 为 local、stackName 为 root、无 outputs;
  • preflight 阶段的 assert 断言在编译时即被求值,不会进入 Terraform 产物——这正是"基础设施代码与业务逻辑统一在一个语言中"的直观体现:纯计算代码不会产生无意义的云资源。

六、sim 运行快照:inflight 测试通过的真实证据

配套的 log2.test.w_test_sim.md 记录了在 sim(模拟器)平台运行 wing test 的完整输出:

pass ─ log2.test.wsim » root/Default/test:inflight absolute

Tests 1 passed (1)
Snapshots 1 skipped
Test Files 1 passed (1)
Duration <DURATION>

解读这份日志:

  • 测试标识 log2.test.wsim » root/Default/test:inflight absolute 表明:源文件被编译为 .wsim 模拟器产物,测试挂在 root/Default 应用树下,对应源文件中 test "inflight absolute" 定义的 inflight 测试;
  • Tests 1 passed (1):唯一的 inflight 测试全部通过,证明 math.log2 在模拟器运行期对 3、2、1、0 四个输入值的断言全部成立;
  • <DURATION> 是快照机制刻意占位符,保证输出与执行时长无关,避免快照抖动。

七、快照背后的工程机制:hangar 如何生成并执行这些测试

这些快照并非手工维护,而是由 hangar 测试工具链自动生成。核心生成器位于 tools/hangar/src/generate_tests.ts,它扫描 tests/sdk_tests(及其他测试目录)下每个 .w 文件,自动生成一个对应的 Vitest 测试文件,其中包含两个标准测试用例:

test("wing compile -t tf-aws", async () => {
  await compileTest(".../tests/sdk_tests/math", "log2.test.w", ...);
});

test("wing test -t sim", async () => {
  await testTest(".../tests/sdk_tests/math", "log2.test.w", ...);
});

工作机制可以概括为:

  1. 递归发现:遍历 sdkTestsDir(即 tests/sdk_tests 目录),只处理扩展名为 .w 的源文件;
  2. 双目标测试:对每个 .w 文件生成两个用例——wing compile -t tf-aws(验证编译产物)与 wing test -t sim(验证运行行为),二者分别产出 _compile_tf-aws.md 与 _test_sim.md 两种快照,这正是本文所分析的关联文档的由来;
  3. 快照落盘:测试结果经处理后写入 tools/hangar/__snapshots__/test_corpus/ 目录(快照目录定义见 tools/hangar/src/paths.ts),与源文件保持相同目录结构;
  4. 跳过控制:通过解析源文件开头的元注释(parseMetaCommentFromPath、shouldSkipTest)支持对特定平台或环境跳过测试。

因此,当你在仓库中看到 sdk_tests/math/log2.test.w 对应两份快照时,说明该用例已经过了"tf-aws 编译产物"与"sim 运行行为"双通道的回归验证——任何对 math.log2 实现或编译器的改动,若导致数值偏差或 Terraform 清单变化,快照测试都会立即失败。

八、在本地复现:三步验证 math.log2

如果你想在本地亲手复现这套验证流程(仓库为只读,以下均为查看与运行操作):

  1. 直接运行 SDK 测试:进入 tests/sdk_tests/math 目录,执行 wing test log2.test.w,可在模拟器上看到与快照一致的 1 passed 结果;
  2. 查看编译产物:执行 wing compile -t tf-aws log2.test.w,生成的 main.tf.json 应与本文第五节展示的快照一致(仅有 provider.aws 空配置);
  3. 更新快照(可选):若有意变更后需要刷新快照,可运行 hangar 的 vitest 用例并允许快照更新,工具链会自动重写 tools/hangar/__snapshots__/ 下对应文件,保持快照与实现同步。

九、小结

围绕 math.log2 这一个函数,我们可以串起 Wing 工程的完整技术链路:

  • 使用层:bring math; 后以 math.log2(value: num) 静态调用,返回 num;
  • 语义层:直接映射 JS Math.log2,支持 log2(3)≈1.58496…、log2(2)=1、log2(1)=0 等典型取值,且 log2(0) 返回 -math.INF 负无穷;
  • 验证层:hangar 自动生成 wing compile -t tf-aws 与 wing test -t sim 双测试,产出 Terraform 编译快照与模拟器运行快照,确保纯计算代码在编译期不产生云资源、在运行期断言全部通过。

这一模式适用于 math 模块全部 33 个标准测试(tests/sdk_tests/math),也体现了 Wing 统一 preflight/inflight 模型下,数学库作为"与平台无关的纯函数"被严格回归测试的工程实践。当你需要在 Wing 中实现算法复杂度分析、二分查找、信息熵计算等涉及以 2 为底对数的场景时,math.log2 就是与语言运行时同源、经过完整快照验证的可靠选择。

登录后查看全文
wing