Skip to content

语言速查 ​

第一次学习 Sprig?请从入门教程开始。本页是当前 stage-0 编译器的快速查阅表。页面中的每一段示例都是 website/snippets/ 下的真实文件,由 scripts/internal/verify-doc-snippets.py 在文档检查时执行。

权威文档是语言规范(设计契约)与 已实现功能状态。当设计稿超出当前实现时, 本页以编译器实际行为为准。

布局与注释 ​

代码块由缩进决定。第一个代码行必须从第 1 列开始,同一个块内缩进必须一致, 制表符会被拒绝(SPR-LEX-TAB)。()、[]、{} 内的换行会被忽略,因此调用和 字面量可以跨行书写。# 开始注释。

绑定 ​

spr
# let binds once; var may be reassigned.
let name = "Ada"
var visits = 0
visits += 1

# Local bindings infer their type from the initializer.
let greeting = "Hello, " + name
print(greeting)
print(visits)

let 只能绑定一次,var 可以重新赋值。局部绑定从初始化表达式推断类型,类字段 必须显式标注类型。Sprig 没有 truthiness:条件必须是 Bool。

函数 ​

spr
# Named functions declare parameter and return types, including -> Unit.
func add(a: Int, b: Int) -> Int:
    return a + b

func factorial(n: Int) -> Int:
    if n <= 1:
        return 1
    return n * factorial(n - 1)

print(add(2, 3))
print(factorial(5))

所有具名函数和方法都要声明参数类型与返回类型,包括 -> Unit。调用使用位置参数。 可能失败的操作通过 throws ErrorType 声明(见下文「错误」)。

类 ​

spr
class Hero:
    let name: String
    var health: Int = 100

    func heal(amount: Int) -> Unit:
        health += amount

    func describe() -> String:
        return name + " (" + health.toString() + " hp)"

# Class construction uses named fields only.
let hero = Hero(name="Ada", health=80)
hero.heal(15)
print(hero.describe())

类用 let(不可变)和 var(可变)声明字段,字段可以有默认值。构造必须使用具名 参数:Hero(name="Ada", health=80);缺失、未知或重复的字段都是编译错误。方法内直接 访问当前实例的字段,参数和局部变量不允许遮蔽字段。

enum、variant 与 match ​

spr
variant Expr:
    Literal(value: Int)
    Add(left: Expr, right: Expr)

enum Mode:
    Fast
    Careful

# match must cover every case; there is no default branch.
func eval(expr: Expr) -> Int:
    match expr:
        case Expr.Literal as node:
            return node.value
        case Expr.Add as node:
            return eval(node.left) + eval(node.right)

func label(mode: Mode) -> String:
    match mode:
        case Mode.Fast:
            return "fast"
        case Mode.Careful:
            return "careful"

let tree = Expr.Add(
    left=Expr.Literal(value=20),
    right=Expr.Literal(value=22)
)
print(eval(tree))
print(label(Mode.Careful))

enum 的 case 不携带负载。variant 声明封闭的 sum type,case 的字段具名且不可变。 语句 match 支持多语句分支;表达式 match 每个分支只包含一个表达式并产生结果。 每个分支写一个 enum 或 variant case,可以用 as node 绑定负载。 缺失、重复、类型错误和不可达的分支都会报错;没有 default 或通配分支,也没有 fallthrough。穷尽性由编译器保证,因此给 variant 增加 case 时,所有漏掉它的 visitor 都会编译失败——仓库里的 AST visitor 实验正依赖这一点。

集合 ​

spr
let immutable: List[Int] = [3, 1, 2]
let mutable: MutableList[Int] = immutable.toMutableList()
mutable.append(4)
mutable.sort()

print(immutable.contains(2))
print(2 in immutable)
print(mutable)
print(mutable[0])
print(immutable) # toMutableList returns a new outer collection

let ages: Map[String, Int] = {"ada": 36, "bob": 41}
print(ages.get("ada"))
print(ages["missing"])
print("bob" in ages)

List[T] 与 Map[K,V] 是只读的,MutableList[T] 与 MutableMap[K,V] 可变。 toMutableList()、toList()、toMutableMap()、toMap() 会生成新的外层集合。 通过不可变类型修改集合会得到 SPR-COLLECTION-IMMUTABLE。索引、in、get、set、 append、sort 以及高阶方法 map/filter/forEach 均已实现。浮点数不能作为 Map 的键,因为 IEEE 相等与哈希在 NaN 和有符号零上不一致。

可空性 ​

spr
# T? marks expected absence. A checked value narrows to non-null inside the branch.
func find(words: List[String], target: String) -> String?:
    for word in words:
        if word == target:
            return word
    return null

let found = find(["alpha", "beta"], "beta")
if found != null:
    print("found " + found)
else:
    print("not found")

let missing = find(["alpha"], "gamma")
print(missing == null)

null 只能赋给 T?。在确认 != null 的分支内,值会被收窄为非空;把可能为空的值 用于非空场景会得到 SPR-TYPE-NULLABLE。可变字段不会跨调用被收窄。Java 引用结果 保守地视为可空,见 JVM 互操作。

错误 ​

spr
# Recoverable failures use typed errors: func ... throws T, throw, try/catch.
func checkout(quantity: Int) -> Int throws Error:
    if quantity <= 0:
        throw Error("quantity must be positive")
    return quantity * 3

try:
    print(checkout(4))
catch problem: Error:
    print("rejected: " + problem.message)

try:
    print(checkout(0))
catch problem: Error:
    print("rejected: " + problem.message)

函数用 throws 声明可能抛出的错误类型。调用方必须用 try/catch(可选 finally) 处理,或者声明同样的效果,否则会得到 SPR-FLOW-THROWS。Error 提供 message 字段。Java 受检异常可以作为导入的 Java 异常类被捕获。

显式泛型 ​

spr
# v0.8 generics: one or more parameters per block, explicit [Type] at every use.
generic T:
    class Box:
        let value: T

generic T:
    func identity(value: T) -> T:
        return value

let box = Box[Int](value=41)
print(identity[Int](box.value) + 1)

用户自定义类、variant 与函数支持 generic T: 或 generic K, V: 块。 每次使用须写显式类型参数,如 Box[Int](value=42);参数不变、没有泛型推断。 类型参数上的相等比较需要 requires T: Equatable。 完整约定见泛型指南。

Lambda ​

spr
# Lambdas are expressions: fn(params) => expression. Arities 0 through 3.
let doubled = [1, 2, 3].map(fn(x: Int) => x * 2)
print(doubled)

let evens = [1, 2, 3, 4].filter(fn(x: Int) => x % 2 == 0)
print(evens)

let collected: MutableList[Int] = []
[1, 2, 3].forEach(fn(x: Int) => collected.append(x * 10))
print(collected)

Lambda 是表达式:fn(x: Int) => expression,支持 0 到 3 个参数,函数体是单个表达式, 不能声明 throws。捕获 var 局部变量会被拒绝(SPR-TYPE-CAPTURE),请先复制到 let 绑定。

JSON 对象查找 ​

spr
import "@std/json.spr" as json

let document = json.parse("{\"name\":null}")
match json.find_member(document, "name"):
    case json.Lookup.Missing:
        print("missing")
    case json.Lookup.Found as member:
        print(json.stringify(member.value))  # prints null
    case json.Lookup.NotObject:
        print("expected an object")

json.find_member 区分 Missing、Found(value: json.Value) 与 NotObject。 已存在的 JSON null、false、零和空字符串均是 Found;对象重复键仍抛出 Error, 成员顺序保持不变。完整解析、查找与序列化边界见 标准层约定(英文)。

函数类型 ​

函数类型显式写出参数类型与返回类型:

spr
let inc: fn(Int) -> Int = fn(x: Int) => x + 1
print(inc(4))
let optional: (fn(Int) -> Int)? = null

fn(Int) -> Int 是类型,fn(x: Int) => x + 1 是值表达式。 支持 0–3 个参数;参数与返回类型保持不变性,没有函数子类型、隐式转换或无类型逃生口。 外层可空须写 (fn(Int) -> Int)?;fn(Int) -> Int? 表示返回值可空。 调用可空函数值前,须按普通规则判空收窄。函数类型可用于局部注解、字段、参数、返回类型 与显式泛型参数。函数类型不能声明 throws;非抛错 callable 内须处理受检错误。

JVM 桥接仅针对对应的 Sprig 自有 sprig.runtime.Fn0–Fn3 形参签名及受支持的 具体类型参数;不会把函数转换为任意 Java Function、Consumer、Runnable 或接口。 用 sprig api <Class> --json 查询实际签名。

模块 ​

spr
import "./math_module.spr" as math

print(math.square(7))
print(math.PI_APPROX)
spr
func square(x: Int) -> Int:
    return x * x

let PI_APPROX: Float = 3.14159

import "./file.spr" as alias 导入另一个 Sprig 文件。import 必须出现在任何声明或 语句之前;被导入模块只初始化一次;导入环会得到 SPR-NAME-IMPORT-CYCLE。导入 Java 类使用同样的语法,只是换成全限定类名:import java.time.LocalDate as LocalDate。

尚未实现 ​

泛型推断、variance、语言内继承与接口、%=、元组与解构、字符串插值都尚未实现; source 数组语法、变长参数与 wildcard 形状不在 JVM interop profile 内(数组仍可作为 不透明外部值传递,见 JVM 互操作)。完整列表见 已知限制,后续规划见 Stage-1 路线图(英文)。

保守的易用性改进 ​

sprig fmt 提供确定性、保留注释、无配置的格式化。模块可用 export alias.Symbol 显式重导出声明,且不绕过包的模块导出边界。 表达式 match 每个分支只允许一个表达式;多语句分支继续使用语句 match。 参见 格式化、重导出 和 match 表达式。不提供通配导出或通用块表达式。

Sprig 采用 Apache-2.0 许可证 · v0.5.0-beta.1 已作为 prerelease 发布