Starlark:基于 Python 语法的嵌入式脚本语言

Starlark 是一种受 Python 强烈影响的动态脚本语言,可以理解为“去掉 Python 大部分系统能力和复杂特性、保留熟悉语法,并专门用于配置、规则和宿主程序扩展的脚本语言”。

Starlark 最初为 Google 的 Bazel 构建系统(类比为 Make,2015 年开源)设计。它的定位不是开发通用应用,而是让宿主程序通过脚本表达配置、规则和扩展逻辑。Starlark 通常被嵌入宿主应用,由宿主程序决定向脚本暴露哪些函数和数据类型。

核心特点

Starlark 的语法基本是 Python 的一个子集,但语言能力明显收敛。它支持动态类型、自动内存管理、词法作用域、一等函数,以及 listdicttupleset 等高级数据类型。
Go 实现目前明确包含 Noneboolbytesintfloatstringlisttupledictsetfunction 等类型。

与 Python 相比,Starlark 没有 class、异常机制、反射、并发等复杂语言特性,也没有默认的文件、网络、操作系统 API。它强调程序短生命周期、无外部副作用和结果可重复,因此特别适合配置和规则执行。

另一个重要特征是确定性(Determinism):核心语言不会隐式读取当前时间、随机数等外部状态,使相同输入能够得到一致结果,这也是 Bazel 选择它的重要原因。

基本语法

Starlark 对 Python 开发者非常友好:

name = "Starlark"

ports = (80, 443)

config = {
    "host": "example.com",
    "enabled": True,
}

def add(a, b):
    return a + b

for port in ports:
    if port == 443:
        print("HTTPS:", port)

支持条件、循环、函数、列表、字典、元组、切片、解包等常用语法。例如:

def build(name, replicas=1):
    return {
        "name": name,
        "replicas": replicas,
    }

services = [
    build("api", 3),
    build("worker", 2),
]

因此它的学习成本很低,但不能把普通 Python 程序直接拿过来运行。

使用场景

最典型的应用是 Bazel。Bazel 使用 Starlark 编写 BUILD 文件和 .bzl 扩展规则,用于描述目标、依赖关系、构建规则以及自定义构建逻辑。

除此之外,Starlark 很适合:

  • 构建系统和 CI/CD DSL
  • 配置文件
  • 规则引擎
  • 插件系统
  • 用户自定义策略
  • Go/Java/Rust 应用的脚本扩展

其核心模式是:

宿主程序
   ↓
注册函数 / 对象 / 类型
   ↓
Starlark 解释器
   ↓
执行用户脚本
   ↓
返回结果

这也是它特别适合 Go 项目的原因。官方 Go 实现 go.starlark.net/starlark 可以直接嵌入 Go 程序。
如果要在 Go 中运行用户提供的简单脚本,同时不允许脚本访问操作系统,Starlark 比直接嵌入 CPython 更符合这种架构。不过“没有系统 API”并不等于完整安全沙箱,生产环境仍应限制脚本执行时间、内存、递归深度和宿主暴露的 API。

版本历史

Starlark 最初叫 Skylark,约在 2015 年随着 Bazel 发展起来,最初实现采用 Java。后来出现 Go 实现,并逐渐将语言规范从 Bazel 的具体实现中独立出来。

2017 年,Go 版本开始发展,形成现在的 starlark-go 项目。
2018 年,由于商标原因,Skylark 正式更名为 Starlark,同时建立独立的语言规范仓库,强调“语言规范”和“具体实现”分离。

目前 Starlark 更适合理解为一套语言规范 + 多种实现,而不是类似 Python 那样严格按照 3.x 演进的单一运行时。Go、Bazel/Java 等实现持续保持兼容和趋同。

如果你有魔法,你可以看到一个评论框~