语言速查
第一次学习 Sprig?请从入门教程开始。本页是当前 stage-0 编译器的快速查阅表。页面中的每一段示例都是 website/snippets/ 下的真实文件,由 scripts/internal/verify-doc-snippets.py 在文档检查时执行。
权威文档是语言规范(设计契约)与 已实现功能状态。当设计稿超出当前实现时, 本页以编译器实际行为为准。
布局与注释
代码块由缩进决定。第一个代码行必须从第 1 列开始,同一个块内缩进必须一致, 制表符会被拒绝(SPR-LEX-TAB)。()、[]、{} 内的换行会被忽略,因此调用和 字面量可以跨行书写。# 开始注释。
绑定
# 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。
函数
# 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 声明(见下文「错误」)。
类
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
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 实验正依赖这一点。
集合
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 和有符号零上不一致。
可空性
# 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 互操作。
错误
# 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 异常类被捕获。
显式泛型
# 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
# 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 对象查找
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, 成员顺序保持不变。完整解析、查找与序列化边界见 标准层约定(英文)。
函数类型
函数类型显式写出参数类型与返回类型:
let inc: fn(Int) -> Int = fn(x: Int) => x + 1
print(inc(4))
let optional: (fn(Int) -> Int)? = nullfn(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 查询实际签名。
模块
import "./math_module.spr" as math
print(math.square(7))
print(math.PI_APPROX)func square(x: Int) -> Int:
return x * x
let PI_APPROX: Float = 3.14159import "./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 表达式。不提供通配导出或通用块表达式。
