☰
Winglang 语言需求解析:以编译期合成、资源抽象与 inflight 为核心的云应用编程模型
2026/10/13 1:43:15 网站建设 项目流程
  • 编程语言
  • 编译器
  • 云原生
  • 语言运行时

【免费下载链接】wing

A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡

项目地址:https://gitcode.com/gh_mirrors/wi/wing
点击查看免费下载

本文基于 wing 仓库中已批准(Approved)的需求级 RFC《Language Requirements》(原文见 docs/contributing/999-rfcs/2022-05-28-winglang-reqs.md)整理而成。该 RFC 发布于 2022-05-28,由 @eladb 提交,是 winglang 语言体验的需求清单而非语法设计——它从"云应用到底需要什么样的编程语言"出发,确立了 wing 语言的核心设计约束。后续的语言规范 RFC 才负责具体语法与文法。本文将逐条展开这些需求,并结合当前仓库中的编译器(wingc)、SDK 源码与测试用例,说明它们如何落地为今天 winglang 的真实能力。读完本文,你将理解 winglang 的"编译输出是基础设施而非可执行文件"这一根本设计,掌握资源(resource)的组合与捕获(lift/capture)机制、inflight 运行时代码模型、依赖注入与合成目标(target/platform)等核心概念,并能在实际工程中借助这些心智模型写出可移植、可组合的云应用。

文档定位:一份"需求",而非"设计"

这份 RFC 的标题明确写着"Language Requirements (approved)",其定位可以从文档开头的自我描述中读出:

This RFC lists therequirementsfrom the wing language experience, not the specific syntax/grammar or design of the language. The language design will follow in a separate RFC.

也就是说,它回答的是"winglang 必须做到什么",而不是"winglang 长什么样"。每个小节都带有一个需求标签(requirement tag),例如w:program-output、w:inflight,这些标签被设计成可以在未来的文档、路线图、GitHub issue 中直接引用,并且原文为每个标签生成了 HTML 锚点(如#w:program-output)。文末还附有一份完整的 需求清单,按标签排序,便于检索。

这种"标签化需求"的做法让语言演进有了可追溯的契约:每个能力点都有唯一编号,设计评审、实现进度跟踪都围绕这些标签展开。在阅读本文时,建议把每个Reqtag当作 winglang 的一条设计契约来理解。

编译输出不是程序,而是"一组文件":desired-state 编程模型

编译器合成任何文件与目录结构

RFC 的第一条核心需求是w:program-output:wing 程序编译后的产物不是运行在单台机器上的可执行文件,而是由你的代码在编译期间"合成(synthesize)"出来的一组文件。

Reqtag:w:program-output

在最基本的层面,wing 编译器可以合成任意文件与目录结构;当用于构建云应用时,这些文件通常是:

  • 基础设施定义(CloudFormation、Terraform、Kubernetes manifests)
  • Dockerfile
  • 函数代码包(function code bundles)
  • 部署工作流
  • 以及其他一切把应用交付到云端所需的产物

关键约束是:wing 对云厂商与供给引擎(provisioning engine)都不设偏好(not opinionated)。它既能支撑 AWS、GCP、Azure 等任意云提供商,也支持 Terraform、AWS CloudFormation、Pulumi 等任意供给引擎。这一条为后文"合成目标"与"依赖注入"两大需求埋下了伏笔。

RFC 用一个最简单的例子演示这个模型。hello.w:

bring std; let hello = std.TextFile("hello.txt"); hello.add_line("Hello, world!");

编译并查看产物:

$ wingc hello.w $ cat hello.txt Hello, world!

看起来平淡无奇。但有趣的事情发生在修改代码之后。把文件名改成hello2.txt再编译:

let hello = std.TextFile("hello2.txt"); hello.add_line("Hello, world!")
$ wingc hello.w $ cat hello.txt cat: hello.txt: No such file or directory $ cat hello2.txt Hello, world!

hello.txt消失了!这在传统语言中不可能发生——如果你用write_file()换个新名字写文件,旧文件依然存在。而 wing 编译器却主动删除了旧文件,因为它把程序视为对"期望状态(desired state)"的描述,而非一系列命令。

剪枝(Prune)与状态文件

更"魔法"的演示是循环生成文件example2.w:

for i in 0..7 { let file = fs.TextFile("output/${i}/file-${i}.txt"); file.add_line("hello, ${i}!"); }

编译后find output能看到 0~6 共 7 个文件;把循环改成0..2再编译,输出只剩 3 个文件——编译器基于新定义删除了不再需要的文件。

Reqtag:w:prune

实现这个能力的机制是状态文件(state file):wing 编译器维护一个形如hello.w.state的文件,记录哪些文件是由代码合成的。下次编译时,编译器据此知道哪些旧文件应当被删除。RFC 同时注明:状态文件并非强制,如果所有产物都在dist或target目录下,状态文件可以是冗余的,也可以直接放在该目录内。

生成文件只读 + 哈希校验

为了不让用户误改生成物,wing 还有一条配套需求w:readonly:

Reqtag:w:readonly

  • 所有由 wing 生成的文件总是以只读(read-only)方式创建;
  • 生成文件的内容哈希被记录在状态文件中;
  • 如果某个文件在 wing 之外被修改过,编译器不会覆盖它。

这三条需求(w:program-output、w:prune、w:readonly)共同定义了 winglang 最根本的心智模型:写 wing 代码 = 声明系统的期望状态,编译器负责让文件系统收敛到这个状态。这与 CDK/Terraform 对云端资源的"收敛"逻辑一脉相承,但 wing 把它下沉到了通用语言层面——任何文件、任何资源都遵循同一套收敛语义。

Resources:面向 desired-state 的组合机制

组合与确定性地址

软件抽象的关键是组合(composition)。wing 引入了**资源(resources)**作为 desired-state 的面向对象组合机制,其基础是 AWS CDK 项目中发展起来的Construct 编程模型(CPM)。该模型已被证明是"用代码建模云资源"的强大机制,并催生了 CDK for Kubernetes、CDK for Terraform、Projen 等一系列 desired-state 框架。

Reqtag:w:resource-definition

资源具备经典面向对象类的全部能力(初始化器、方法、属性、继承……),但有一个让它们天然适合定义 desired-state 的独特属性:跨编译的确定性地址(deterministic address)。传统 OO 语言中对象实例也有"地址",但那是内存地址——程序每次执行都会重新分配,是转瞬即逝的。而 wing 资源可以跨编译期映射到同一个资源,这正是 construct 编程模型的核心。wing 直接复用 constructs 基础库(所有 CDK 的基座库),从而与整个 CDK 生态互操作。

在 wing 中,资源的**定义(实例化)**非常简单:

let my_resource = MyResource()

Reqtag:w:resource-definition

熟悉 TypeScript/Java/Python 的读者知道,construct 类的前两个初始化参数通常是scope和id,它们是资源组合与确定性地址的关键。但 wing 在多数情况下允许省略这两个参数,从而降低认知负担、减少出错机会:

resource NotifyingBucket { init() { let bucket = Bucket() let topic = Topic() } }

Reqtag:w:resource-default-scope

Reqtag:w:resource-default-id

省略时,wing 默认以this作为scope、以类型名作为id。作为对照,等价的 TypeScript 写法是:

const bucket = new s3.Bucket(this, 'Bucket'); const topic = new sns.Topic(this, 'Topic');

自定义 id 与自定义 scope

省略默认值适用于大多数场景,但也支持按需定制。显式指定资源标识符使用be "ID"语法:

let bucket = Bucket() be "MyBucket";

Reqtag:w:resource-custom-id

典型场景是同一 scope 下有多个同类型资源——比如循环创建 10 个 Topic:

let topics = mut_list<Topic>(); for i in 0..10 { topics.add(Topic() be "Topic-${i}") }

RFC 特别提醒:自定义 id 仍然只是scope 内唯一,并不是资源的全局地址。控制云端资源全局地址是引擎(engine)相关的能力,例如在 AWS CDK 中可以用CfnResource.overrideLogicalId()覆盖 CloudFormation 逻辑名。

显式指定资源 scope 则使用in SCOPE语法:

Topic() in alternative_scope

Reqtag:w:resource-custom-scope

它在运行时动态构造资源树时非常有用,例如单元测试中:

let app = aws.App(); let stack = aws.Stack() in app; let my_bucket = s3.Bucket() be "MyBucket" in stack; let template = app.synth().template; expect(template).to(...);

资源类型声明

对应地,在声明资源类型时,wing 也不需要显式写出scope、id位置参数,更不需要继承constructs.Construct基类:

resource Foo { init() { } }

Reqtag:w:resource-declaration

其等价的 TypeScript 写法为:

class Foo extends Construct { constructor(scope: Construct, id: string) { super(scope, id); } }

wing 用更少的样板代码表达了同样的结构,把 construct 的约定内化进语法。

资源树反射:node_of 与确定性地址

资源树最有价值的一点是它提供了丰富的反射编程模型。反射 API 挂在每个资源的constructs.Node上,通过node_of(c)访问:

Reqtag:w:resource-node

let node = node_of(my_bucket); assert(node.scope == stack); assert(node.id == "MyBucket"); assert(node.addr == "c876fd36dd614e466ada94591af0f00e3600fe3648");

node.addr就是该资源在程序内唯一的确定性地址,合成器(synthesizer)用它生成跨执行稳定的逻辑标识符(如 CloudFormation Logical ID)。这一需求在当前仓库 SDK 中有明确实现——packages/@winglang/sdk/src/std/node.ts 中:

  • scope:返回该 construct 所在作用域(根节点为undefined);
  • id:返回当前 scope 内唯一 id;
  • path:返回树中的绝对路径,组件以/分隔;
  • addr:返回 42 字符的十六进制树唯一地址,以c8开头,由构造路径各组件经SHA-1计算得出;同时为支持树重构,id 为Default的构造会被排除在计算之外。

对照 RFC 中示例地址c876fd36dd614e466ada94591af0f00e3600fe3648,可以确认它就是"c8 + 40 位十六进制"的格式——需求文档中的示意值如今已被源码精确实现。

根资源(Root Resource)与合成目标

每个 wing 程序都有一个隐式的根资源(root resource),它是主程序中定义资源的默认scope,相当于 CDK 中的Appconstruct:

Reqtag:w:resource-implicit-root

编译器会根据**编译目标(compilation target)**来操纵这个根资源。RFC 给的例子是test.w:

bring cloud; cloud.Bucket();

用cloudformation目标编译:

$ wingc test.w --target aws-cloudformation

得到cdk.out/Default.template.json:

{ "Resources": { "Bucket83908E77": { "Type": "AWS::S3::Bucket", "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" } } }

Reqtag:w:synthesis-target

目标(target)本质上告诉编译器如何配置根资源,使云资源被解析为 AWS CloudFormation 资源并合成出对应模板;如果换成tf-gcp目标,输出则变成在 Google Cloud Platform 上部署该 bucket 的 Terraform 配置。这就是前文"不绑定厂商与引擎"需求的具体落地。

这一设计在当前仓库中演进为platforms(平台)机制,见 docs/docs/03-platforms/01-understanding-platforms.md:wing compile --help可列出内置平台,包括sim(本地模拟测试)、tf-aws、tf-azure、tf-gcp;--platform选项还可多次指定,一次把应用编译到多个平台。

对于自定义需求(如多云、多引擎部署),用户可以提供自定义的根资源实现,初始形态是通过 npm 包引用 JavaScript 模块:

Reqtag:w:synthesis-target-custom

$ wingc test.w --target npm://@acme/wing-targets@^1.2.3/Default

异步终结器(Async Finalizers)

有些资源需要在输出被合成之前执行异步操作——典型的例子是调用进程外打包器(out-of-process bundler)或其他只有异步 API 的第三方代码:

Reqtag:w:resource-async-finalizer

resource MyResource { async fin() { // run some async code here } }

RFC 强调两点:

  1. 终结顺序无法保证,因此终结器应避免修改资源树;
  2. 其实现方式是:在 wing 编译器生成的 preflight 代码中,于异步上下文内await底层synth()方法。

如果未来 wing 支持显式可变性(explicit mutability),编译器将能在终结期间禁止对树的修改:

Reqtag:w:resource-async-finalizer-immutability

Inflight 函数:运行时逻辑成为 desired-state 的一部分

从 preflight 到 inflight

desired-state 不止是云端资源的配置,还包括部署之后在这些资源内部执行的业务逻辑。wing 把"编译期间执行的代码"与"部署后执行的代码"区分为preflight与inflight两个阶段:

Reqtag:w:inflight

wing 默认(且隐式)的执行上下文是preflight——因为云应用真正的入口点是应用架构的定义本身,而不是系统内某台机器上运行的代码。这一定位在官方概念文档中得到了重申:preflight 代码在编译时运行一次,定义基础设施配置,并交由 Terraform、CloudFormation、Pulumi 或 Kubernetes 等供给引擎消费。

"把运行时逻辑作为 desired-state 定义的有机组成部分,并让两个执行域自然交互"是 winglang独特且根本的能力。为此 wing 提供了名为inflight 函数(inflights)的语言原语:一个可以被打包、稍后在各种计算平台上执行的隔离计算单元,它能自然地与 inflight 外部定义的资源交互并调用它们的运行时操作。

RFC 的示例upload.w非常直观:

let bucket = cloud.Bucket(); let topic = cloud.Topic(); let message_count = 4; let fn = cloud.Function(inflight (e: cloud.BucketUploadEvent) => { for i in 0..message_count { topic.publish("new file uploaded to our bucket: ${e.name} - ${i}"); } }); bucket.on_upload(fn);

语义是:当对象上传到 bucket 时,向 topic 发布携带对象键的消息。inflight 闭包作为第一个初始化参数传入cloud.Function(可以把它理解为 AWS Lambda 的抽象版本),闭包内调用的topic.publish()是 topic 的运行时 API。

编译这个程序:

$ wingc upload.w --target cloudformation $ find cdk.out cdk.out/Default.template.json cdk.out/asset.250ffecc3ad9d703e3df52ff035a1ec6ace6d2afaff290c6937b879f0897fc16.bundle/index.js

输出同时包含 CloudFormation 模板(基础设施资源定义)与一个index.js(AWS Lambda 处理器代码及全部依赖)。RFC 指出,如果深入模板与运行时代码,会发现编译器替开发者处理了大量"无差别细节":

  1. 把 topic 的 ARN 通过环境变量传入 AWS Lambda 函数;
  2. 在 Lambda 执行策略中添加sns:PublishMessage权限,且是针对该特定 topic 的最小权限;
  3. 将cloud.Topic的 AWS 实现客户端库打进 Lambda bundle。

与此同时有一条硬性约束:inflight 内不能定义资源——因为 inflight 执行时没有合成器、也没有供给引擎来部署资源:

Reqtag:w:inflight-no-resource-definitions

捕获(Capturing)机制

捕获资源

inflight 可以自然地与定义在闭包之外的资源的运行时 API交互(如上面例子中的topic)。当一个资源被 inflight 捕获后,wing 提供一套反射捕获的 API,让实现方据此响应:例如cloud.Function(proc)的实现会利用它识别 inflight 捕获了哪些资源、调用了运行时 API 的哪些方法,从而接通所需信息并授予恰当的权限:

Reqtag:w:inflight-capture-resources

这一需求在 SDK 源码中体现为onLift(host, ops)钩子:packages/@winglang/sdk/src/std/resource.ts 定义IHostedLiftable接口,编译器为每个需要该对象的 inflight host 调用一次onLift,传入被请求的 inflight 方法列表ops;任何 preflight 类都可以实现该方法,为 host 添加权限、环境变量等能力。官方概念文档《preflight 与 inflight》把被引用的 preflight 对象称为"被 lifted 的对象":编译期间编译器分析 lifting 对象的使用方式(如bucket.put(...)),从而为cloud.Function生成恰好覆盖put的写权限——这就是 winglang 生成最小权限的机制。编译器的 lifting 分析逻辑位于 packages/@winglang/wingc/src/lifting.rs,仓库中也有大量对应测试,如 tests/valid/explicit_lift_qualification.test.w。

捕获不可变数据

不可变的原始值(primitive)也可以被 inflight 引用并捕获:这种情况下数据会被复制并作为静态值打进打包输出。包含 token 的原始值同样可被捕获,并会在部署期间通过环境变量(或其他动态运行时值手段)解析:

Reqtag:w:inflight-capture-data

需要当心的是:捕获集合(list/map)时,集合里可能引用资源,此时需要连带捕获这些资源。RFC 给出了一个编译器当前无法处理的例子:

let bucket_per_region = { US: Bucket(region: 'us-east-1'), EU: Bucket(region: 'eu-west-2'), }; let handler = inflight (event) => { let bucket = bucket_per_region.get(event.region); await bucket.upload('boom', 'bam'); };

handler 事实上捕获了 map 里的两个 bucket,但由于bucket.upload()是间接发生的(先经map.get()取出再调用),编译器很难判定这是对捕获资源的调用。这需要深度的静态分析(编译器必须理解map.get()返回的是 map 的值),未来可能实现。

当编译器无法判定捕获性质时,必须报错,并给出**显式捕获(explicit capture)**的语法作为逃生舱:

let handler = inflight (event) => { let bucket = bucket_per_region.get(event.region); ^--------- ERROR: map elements must be captured explicitly await bucket.upload('boom', 'bam'); };

Reqtag:w:inflight-explicit-capture

显式捕获写法:

let handler = inflight (event) => { let bucket = bucket_per_region.get(event.region); await bucket.upload('boom', 'bam'); } captures [ { obj: bucket_per_region.US, methods: ["upload"] }, { obj: bucket_per_region.NA, methods: ["upload"] }, ];
间接资源捕获

再看一个更隐蔽的场景——捕获一个用户自定义资源,而它背后还藏着另一个资源:

resource DenyList { _bucket: cloud.Bucket; init() { this._bucket = cloud.Bucket(); } inflight _map: any; inflight init() { this._map = this._bucket.download_json("deny-list.json"); } inflight is_blocked(name: str, version: str): bool { return this._map[name] ?? this._map["${name}/v${version}"]; } } let deny_list = DenyList(); let handler = inflight (event: any) => { if deny_list.is_blocked(event.name, event.version) { print("${event.name}@${event.version} is blocked"); } };

这里handler捕获了deny_list,而deny_list内部使用了一个 bucket。捕获deny_list时,编译器需要隐式地把背后的 bucket 一起捕获:

Reqtag:w:inflight-capture-indirect

禁止捕获可变对象

上述捕获规则蕴含一条推论:普通对象不能被 inflight 闭包引用,因为 wing 中的对象理论上可以通过方法调用变得可变。资源除外——它们一旦部署即不可变,捕获时通过其运行时客户端捕获:

Reqtag:w:inflight-capture-forbid-mutable

下面这段代码不会编译通过:

class MyClass { mutate_me() { } } my_class := MyClass() inflight fn bar() { my_class.mutate_me() // ^-- ERROR: trying to capture mutable object }

RFC 备注了一条 TODO:未来可以考虑为对象引入显式不可变性,从而允许对不可变对象做 marshalling。从设计意图看,捕获规则的核心目标是保证"部署后不可变"这一不变式——这让编译器可以安全地把捕获内容固化为静态值或运行时客户端,也让最小权限计算成为可能。

inflight 内的静态变量

inflight 函数常常需要在多次执行之间持有状态——在 AWS Lambda 等平台上,这种状态可当作短期缓存使用。wing 借鉴 C 语言的static关键字支持这一需求:

Reqtag:w:inflight-static

inflight fn my_handler() { static big_blob := download_big_blob() // use big_blob }

上面的big_blob会被定义为 AWS Lambda 函数的全局变量,在同一个 Lambda 实例(server)内的多次执行之间保持不变。

依赖注入:让抽象资源在编译期解析

wing 的一个主要目标是让开发者写出可移植(portable)的云应用。为此,wing 支持定义抽象资源,只在编译期间解析其具体实现:

Reqtag:w:dependency-injection

abstract resource Bucket { abstract make_public(): void make_public_and_print() { this.make_public() print("boom") } } my_bucket := Bucket() my_bucket.make_public_and_print()

编译这段程序:

$ wingc prog.w prog.w:5:ERROR: unable to resolve abstract resource `Bucket`

编译器明确告知:它不知道如何解析程序中用到的抽象Bucket资源,需要在编译时"注入"实现。RFC 规划了多种解析方式:编译器命令行、声明式解析文件/库,或附加代码(TODO)。其命令行草图:

$ wingc prog.w --resolve "Bucket=aws.s3.Bucket"

结合前文的"合成目标"需求可以看到 winglang 的架构脉络:根资源负责把资源解析到具体引擎(target),抽象资源 + 依赖注入负责把资源解析到具体实现(implementation)。两者配合,同一份 wing 代码可以在 AWS/GCP/Azure 间迁移而不改动业务逻辑。

Runtime Client API:资源的运行时客户端

如上所述,inflight 与资源交互时只能使用其运行时 API(runtime API)——可以把它理解为资源的"客户端(client)",这也是许多系统的建模方式。wing 资源类型可以同时声明 preflight 部分(如基础设施)与 inflight 部分(如运行时客户端):

Reqtag:w:inflight-clients

bring 'aws-sdk' as awssdk; bring 'aws-cdk-lib' as awscdk; resource Users { _table: awscdk.dynamodb.Table; // preflight initializer (constructor) init() { this._table := awscdk.dynamodb.Table(partition_key: "id"); } inflight _client: awssdk.DynamoDB; // inflight initializer inflight init() { this._client := aws.DynamoDB(); } // inflight "client" inflight add_user(id: string, name: string, last: string) { await this._client.put_item( TableName: this.table.table_name, Item: { ID: { S: id }, UserName: { S: name }, LastName: { S: last }, }, ); } } let users = Users(); let new_users = cloud.Queue(); new_users.add_consumer(cloud.Function(inflight (e) => { users.add_user(e.user_id, e.name, e.last); }));

这段代码演示了 wing 的"双阶段资源"形态:init()是 preflight 初始化器(构造基础设施),inflight init()是运行时初始化器(构造客户端),inflight add_user(...)是暴露给其他 inflight 的运行时方法。SDK 中cloud.Function的实现正是围绕"inflight client"展开的:packages/@winglang/sdk/src/cloud/function.ts 中handler._toInflight()获取 handler 的 inflight 客户端引用,再在 bundle 中生成客户端初始化代码;SDK 还通过IInflightHost.addEnvironment()接口(packages/@winglang/sdk/src/std/resource.ts)向 host 注入环境变量——这正是 RFC 提到的"把 ARN 通过环境变量传入 Lambda"的落地接口。

可观测性:Metrics、Alarms、Logs、Tracing

可观测性是任何云应用的基础能力。wing(及其标准库)对下列可观测性特性提供开箱即用支持,并且拥抱OpenTelemetry:

  1. Metrics——指标可以用语法定义并上报(如item_count++);
  2. Alarms——用户可以在任意指标上定义告警,告警定义的语法是强类型且编译期检查的;
  3. Logs——任意 inflight 可在运行时发出日志,并能以贯穿整个系统的单一流(single flow)被 tail 与查看;
  4. Tracing——一个 trace 标识符被隐式传递给所有网络调用,可观测工具可借此提供跨分布式组件的实时追踪。

对应需求标签:

Reqtag:w:observability-metrics

Reqtag:w:observability-alarms

Reqtag:w:observability-logs

Reqtag:w:observability-tracing

不透明原语(Tokens):部署期才知道的值

tokens 是不透明(opaque)的原始类型,包含只能在部署期间解析的值。wing 类型系统内置对 token 的支持,目的是保护用户不会意外篡改这些值:

Reqtag:w:tokens

RFC 给出的实现构想是让String派生自OpaqueString,后者不提供.length、切分或读取内容的能力:

bucket := Bucket() print(bucket.name.length) /// ----------------^ // can't take the length of an opaque value inflight fn handler() { print(bucket.name.length) print(bucket.name) }

这里bucket.name是部署期才解析的 token,因此在 preflight 中不能对它有.length之类的操作——这从类型层面杜绝了把 token 当普通字符串拼接、截断的经典错误。

互操作性与类型系统

wing 的生态战略建立在 JSII(JavaScript Interface Interop)之上:

Reqtag:w:interop-jsii

  • wing 库本质上就是 JSII 库;
  • JSII 库可以在 wing 代码中原生导入使用;
  • CDK 资源可以自然地用在 wing 资源中,反之亦然;
  • 任何 TypeScript 库都可以在 wing 中使用,TypeScript 类型信息会被用来提供强类型。

类型系统方面:

Reqtag:w:typesystem

  • 与 JSII 类型系统兼容;
  • 支持 Duration/Size 字面量(如5s、19GiB);
  • 支持 JSON 字面量。

这条互操作需求让 wing 不必从零建设生态——它站在 CDK、constructs 与整个 JS/TS npm 生态的肩膀上。

Preflight 警告与错误:编译期即合约检查

wing 的 preflight 代码在编译期间执行,这意味着它可以在此期间发出错误或警告,并以编译器诊断(compiler diagnostics)的形式呈现在编译输出和 IDE 工具中:

Reqtag:w:preflight-errors

RFC 示例:

struct MyResourceProps { max_len: number; name: string; } resource MyResource { init(props: MyResourceProps) { if props.name.length > props.max_len { throw("`name` is too long (${props.name.length} > ${props.max_len})") } if props.name.length == props.max_len { print("Be careful, your name is at the maximum length") } } }

当消费者传入非法参数:

let my = MyResource(name: "hello", max_len: 3) // ^---- ERROR: `name` is too long (5 > 3)

这是一种非常强大且常见的场景:某些逻辑约束无法用类型系统表达,但它们是某个类型契约(前置条件)的组成部分——preflight 的throw让这些前置条件成为可编译期验证的强约束。

RFC 同时列出了待解决的开放问题:

  • 应当能区分警告(warning)与错误(error);
  • 从控制流角度,错误时用throw/raise跳出流程是合理的,但警告时不应跳出;
  • 可借鉴 AWS CDK 的 construct tree annotations 机制。

展望:Wishlist 中的演进方向

RFC 末尾列出了 wing 演进过程中将考虑的候选特性(Wishlist),摘要如下:

  • 逃生舱(Escape Hatches):考虑内置机制,允许绕过抽象直接操作底层(如 CloudFormation 层)。
  • REST、GraphQL 与微服务:用类型系统定义 GraphQL/REST 端点,自动生成 OpenAPI 或 GraphQL 规范以及多语言客户端库;让两个微服务跨 API 边界交互,减少服务发现与互调的样板代码。
  • 工作流(Workflows):让 preflight 代码反射 inflight 块内部代码,将其转换为分布式工作流引擎(如 AWS Step Functions、Apache Airflow)的定义,也可用于生成 CI/CD 工作流(如 GitHub Actions)。functionless项目正用 TypeScript 探索这一方向。
  • Web3:评估 wing 是否可用于构建包含区块链智能合约的系统,或编译到 Solidity。
  • 云端数据结构(Cloud data structures):提供实现于云端的一等语言原语——map 用键值存储实现、全局变量用分布式计数器实现等。
  • 前端开发:网站是云应用的组成部分,wing 有望从后端扩展到前端逻辑,消除跨越两个领域时的样板与胶水代码。

说明:以上为 RFC 的需求愿景,属于 2022 年规划的方向,具体能力以当前仓库实现与语言规范为准。

附:完整需求清单

RFC 使用脚本从正文中提取全部Reqtag生成需求清单,方便在 issue、路线图中引用。完整列表如下(锚点均在原文档内):

  • w:dependency-injection
  • w:inflight-capture-data
  • w:inflight-capture-forbid-mutable
  • w:inflight-capture-indirect
  • w:inflight-capture-resources
  • w:inflight-clients
  • w:inflight-explicit-capture
  • w:inflight-no-resource-definitions
  • w:inflight-static
  • w:inflight
  • w:interop-jsii
  • w:observability-alarms
  • w:observability-logs
  • w:observability-metrics
  • w:observability-tracing
  • w:preflight-errors
  • w:program-output
  • w:prune
  • w:readonly
  • w:resource-async-finalizer-immutability
  • w:resource-async-finalizer
  • w:resource-custom-id
  • w:resource-custom-scope
  • w:resource-declaration
  • w:resource-default-id
  • w:resource-default-scope
  • w:resource-definition
  • w:resource-implicit-root
  • w:resource-node
  • w:synthesis-target-custom
  • w:synthesis-target
  • w:tokens
  • w:typesystem

结语

把这份 2022 年批准的需求 RFC 与当前仓库对照,可以清楚地看到 winglang 的设计主线:**编译期合成输出(而非运行可执行文件)**为一切收敛语义(prune、readonly、state file)奠基;resources 与确定性地址让基础设施定义可组合、可跨编译稳定;inflight 捕获模型让运行时业务逻辑融入 desired-state 并自然获得最小权限;依赖注入与合成目标则保证了多云、多引擎的可移植性。这些需求标签至今仍是理解 winglang 每一处语法与 SDK 设计的钥匙——无论你阅读语言规范、preflight/inflight 概念文档,还是直接研读 SDK 源码 与编译器 lifting 实现,都可以回到这份需求文档,找到设计背后的"为什么"。

  • 编程语言
  • 编译器
  • 云原生
  • 语言运行时

【免费下载链接】wing

A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡

项目地址:https://gitcode.com/gh_mirrors/wi/wing
点击查看免费下载

相关推荐

上一篇:Helm:免费跨平台音乐合成器的终极指南
下一篇:【亲测免费】 西安电子科技大学研究生学位论文XeLaTeX模板使用指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询