渲染zh-Hans

Types and values

Data types#

Faber 拥有静态、类型优先的类型系统。每个声明都把类型放在名称之前:文本 名称,而不是 名称: 文本。类型系统涵盖标量原语、泛型集合、定宽数值、张量,以及面向 GPU 的寄存器类型。

原语类型#

类型作用示例字面量
文本Unicode 字符串"Salve, munde"
窄字串固定机器令牌'solum:lege'
i32有符号整数42
f64浮点数3.14
布尔布尔值真, 假
无值单元类型 / 无值—
空类型空值 / 缺失空类型
时刻时长 / 时间点—
json编译期 JSON 值{ "key": "value" }
字节十六进制字节序列\|00ff\|

定宽数值类型#

在类型位置写位宽。这些就是数值类型 — 与 Math in the ether 相同的列表:

族位宽
有符号i8 i16 i32 i64
无符号u8 u16 u32 u64
十进制d64
无界整数inf
浮点f16 bf16 f32 f64
入口 {
    常量 i32 narrow ← 7 ∷ i32
    常量 u64 wide ← 255 ∷ u64
    常量 f32 single ← 1.5 ∷ f32
}

位宽标记本身就是类型。

可空类型#

可空值使用联合语法 T ∪ 空类型:

函数 find(文本 key) → 整数 ∪ 空类型 {
    返回 皆无
}

函数 maybe() → 文本 ∪ 空类型 {
    返回 皆无
}

Faber 中没有 T? 或 Option<T> 语法。联合是显式的。

类型别名#

类型 UserId = 整数

泛型#

函数、类型别名、类 和 契约 接受使用 <T> 语法的类型参数:

函数 identitas<T>(T 动态值) → T {
    返回 动态值
}

函数 primum<T>(列表<T> res) → T ∪ 空类型 {
    返回 res.第一个()
}

支持在调用处显式指定类型参数:

函数 identitas<T>(T 动态值) → T {
    返回 动态值
}

入口 {
    常量 整数 value ← identitas<整数>(7)
}

集合#

类型作用简写
列表<T>有序动态集合lf32, lu32
映射<K, V>键值映射—
张量<T, Figura>稠密定形缓冲区tf32[4], ti64[2,3]
稀疏<T, Figura>稀疏定形缓冲区sf32[4], si64[2,3]
区间区间类型—
集合<T>无序集合—
迭代器<T>惰性流—
入口 {
    常量 列表<整数> nums ← [1, 2, 3]
    常量 映射<文本, 整数> scores ← { "alice": 10, "bob": 20 }
}

张量类型#

张量<T, Figura> 是稠密定形容器:

形式含义
张量<T, Figura>规范写法
张量<T, []>0 阶(标量容器)
张量<T, _>形状推断占位符
张量<T, [N]>1 阶向量
张量<T, [N, M]>2 阶矩阵
入口 {
    常量 张量<f32, []> scalar ← 空集
    常量 张量<整数, [4]> row ← [1, 2, 3, 4] ↦ 张量<整数, [4]>
    常量 整数 ∪ 空类型 first ← row[0]
}

GPU 核心类型#

这些类型由系统通道识别,用于 GPU 和寄存器相关工作。缺乏硬件支持的包目标会拒绝它们:

函数 half(f16 x) → f16 { 返回 x }

函数 add(矩阵<f32, [2, 2]> a, 矩阵<f32, [2, 2]> b) → 矩阵<f32, [2, 2]> {
    返回 a.addita(b)
}

函数 swap(原子<i32> cell, i32 value) → i32 {
    返回 cell.exchange(value)
}

类型上的借用标记#

借用标记(借自、传入、拥有)可以出现在参数位置的类型上,用于表示值的传递方式:

# shared borrow — caller retains ownership
functio imprime(de textus label) → vacuum { }

# mutable borrow — caller lends mutable access
functio duplica(in numerus value) → vacuum { }

# move — caller gives up ownership
functio consume(own textus buffer) → textus {
    redde buffer
}

比较策略#

运算符家族行为
≡, ≠, ≢精确相等要求类型完全相同;空类型 旁路
≅, ≇提升后精确相等数值宽度合并后精确比较
≈, ≉模糊相等容差匹配——isclose 默认(rel_tol 1e-09);仅数值操作数
<, ≤, >, ≥排序数值、时间点、标量文本
内区间包含数值在区间内
间集合归属元素在集合中

Variables and binding#

Faber 拥有三个变量关键字和一个专门的赋值符。核心区别在于 常量(一次写入)与 变量(可自由重赋值),以及 ←(运行时流)与 =(结构体字段形状)。

常量 — 不可变绑定#

常量 绑定只能写入一次。声明时可带或不带初始化式;若声明时不带,则必须在读取之前精确赋值一次。第二次赋值会被拒绝。

入口 {
    常量 整数 count ← 0
    常量 文本 name ← "Marcus"
    常量 _ inferred ← [1, 2, 3]
}

延迟初始化:

入口 {
    常量 整数 factor
    如果 真 {
        factor ← 10
    }
    否则 {
        factor ← 100
    }
    显示 factor
}

变量 — 可变绑定#

变量 绑定可自由重赋值:

入口 {
    变量 整数 count ← 0
    count ← count + 1
    count ← count * 2
}

设 — 推断不可变语法糖#

设 是 常量 _ 的语法糖——一种类型推断的不可变绑定:

入口 {
    常量 _ salve ← "Salve"
    常量 _ 名称 ← "Marcus"
    常量 _ x ← 42

    # Deferred form
    常量 _ label
    label ← "deferred"
}

运行时绑定与结构体定义#

Faber 将大多数语言合并为 = 的语义拆分为两种:

符号角色用途
←运行时流初始绑定、重赋值、变更
=结构形状字面量与元数据内的字段名
类 Point {
    常量 整数 x
    常量 整数 y
}

入口 {
    # Runtime: ← attaches a value to a name at execution time
    变量 整数 count ← 0
    变量 文本 label ← "ready"
    count ← count + 1

    # Structural: = defines field values inside a type literal
    常量 _ p ← Point { x = 10, y = 20 }
}

取自 字段提取#

取自 将值的字段提取为局部绑定:

类 Persona {
    常量 文本 名称
    常量 整数 aetas
}

入口 {
    常量 _ p ← Persona { 名称 = "Marcus", aetas = 30 }
    常量 文本 名称 ← p.nomen
    常量 整数 aetas ← p.aetas
    # prints "Marcus"
    显示 名称
}

更新可变数值#

使用二元运算符 + 和 - 配合运行时赋值 ←,更新可变的 整数 位置。两个运算符都需要两个操作数;它们不是后缀语句:

入口 {
    变量 整数 i ← 0
    # i becomes 1
    i ← i + 1
    # i becomes 0
    i ← i - 1
}

Collections#

Faber 拥有多种由编译器内置的集合类型。其规范方法位于编译器中,而非标准库中。

Lista — 有序动态集合#

入口 {
    常量 列表<整数> empty ← 空集
    常量 _ numbers ← [1, 2, 3, 4, 5]
    常量 _ names ← ["Marcus", "Julia", "Gaius"]
    常量 _ nested ← [[1, 2], [3, 4]]
}

使用 展开 展开:

入口 {
    常量 列表<整数> a ← [1, 2, 3]
    常量 列表<整数> b ← [4, 5, 6]
    常量 _ combined ← [展开 a, 展开 b]
    常量 _ headed ← [0, 展开 a, 99]
}

关键方法:longitudo、accipe、appende、求和、primus、novissimus。

Tabula — 键值映射#

入口 {
    常量 映射<文本, 整数> scores ← { "alice": 10, "bob": 20 }
}

Tensor — 密集定形缓冲区#

入口 {
    常量 张量<f32, []> scalar ← 空集
    常量 张量<整数, [4]> row ← [1, 2, 3, 4] ↦ 张量<整数, [4]>
    常量 整数 ∪ 空类型 first ← row[0]
}

Tensor 语法糖(用于数值密集型代码):

入口 {
    常量 tf32[] seed ← 空集
    常量 tf32[4] lanes ← seed.由扁平构造([1.0, 2.0, 3.0, 4.0], [4])
}

关键方法:forma、accipe、ponde、crea、structa、strue,以及逐元素算术、矩阵乘法(multiplicatio)和归约(求和、productum)。

Sparsa — 稀疏定形缓冲区#

入口 {
    常量 稀疏<f32, [2, 3]> sparse ← 空集
    sparse.ponde([0, 1], 4.0)
    sparse.ponde([1, 2], 9.0)

    # accipe returns the stored value, here 4.0
    显示 sparse.accipe([0, 1])
    # count of stored entries
    显示 sparse.nonnihil()
}

密集与稀疏之间的转换:

入口 {
    常量 tf32[2, 2] dense ← [[1.0, 0.0], [0.0, 2.0]] ↦ 张量<f32, [2, 2]>
    常量 sf32[2, 2] sparse ← dense ↦ 稀疏<f32, [2, 2]>
    常量 tf32[2, 2] roundtrip ← sparse ↦ 张量<f32, [2, 2]>
}

游标 — 惰性流#

迭代器<T> 是一种惰性流类型。可由集合迭代器、接收 视图或生成器函数创建。通过 遍历 取自 消费:

入口 {
    常量 _ items ← [1, 2, 3]
    遍历 取自 items 常量 item {
        显示 item
    }
}

Intervallum — 范围#

# exclusive range: 0, 1, 2, 3, 4
遍历 范围 0‥5 常量 i {
    显示 i
}
# inclusive range: 0, 1, 2, 3, 4, 5
遍历 范围 0…5 常量 i {
    显示 i
}

‥ 是排他范围的端点;… 是包含端点。

String and template literals#

Faber 使用分隔符语义 — 每种引号形式表示不同的源代码形态。它们之间不能互换。

字面量形式#

形式类型角色
'…'窄字串固定机器令牌;无 §;无 (…)
"…"文本短 Unicode 单行字符串;(…) 渲染
«…»文本块/多行 Unicode;(…) 渲染
… forma捕获模板;(…) 捕获
{ … }json编译期 JSON 文档
`…`字节编译期十六进制字节
[ … ]列表<T>Faber 列表字面量

字符串模板应用#

Faber 通过字符串模板应用格式化文本:一个带有 § 占位符的 "…" 或 «…» 字面量,后接括号参数:

函数 greet(文本 名称) → 文本 {
    返回 "Salve, §!"(名称)
}

入口 {
    常量 整数 pagina ← 3
    常量 整数 totum ← 10
    常量 文本 code ← "200"
    常量 文本 label ← "OK"
    常量 _ msg ← "Page § of §"(pagina, totum)
    常量 _ block ← "status: § (§)"(code, label)
}

关键规则:

  • § (U+00A7) 是模板占位符
  • 位置占位符:§0、§1、…… 用于显式排序
  • 结尾的 ! 选择显示格式化:"Salve, §!"(名称)
  • (args) 后缀是模板应用,不是函数调用

块字符串#

多行块使用书名号 «…»:

入口 {
    常量 _ sql ← «
        select id, email
        from accounts
    »
}

捕获模板 (forma)#

反引号模板捕获文本和参数而不渲染。 对于绑定的 SQL/URL 载荷是安全的:

入口 {
    常量 整数 user_id ← 42
    常量 _ query ← `select * from users where id = §`(user_id)
}

内联 JSON#

裸 { … } 是内联 JSON:一个编译期的 json 文档,不是匿名的 Faber 对象。键是用 : 分隔的带引号字符串:

入口 {
    常量 _ empty ← {}
    常量 _ user ← { "name": "Marcus", "age": 30, "active": true }
    常量 _ nested ← { "meta": { "version": 1 }, "tags": ["alpha", "beta"] }
}

若要构造有类型的属,请使用类型名和 = 字段形态:

类 Point {
    常量 整数 x
    常量 整数 y
}

入口 {
    常量 _ p ← Point { x = 10, y = 20 }
}

Nullability and optionality#

Faber 在值层面区分"空缺"与声明位置的"可选提供"。

可空值 — T ∪ 空类型#

当值可能缺失时,使用 T ∪ 空类型:

函数 find(文本 key) → 整数 ∪ 空类型 {
    返回 皆无
}

函数 divide(整数 a, 整数 b) → 整数 ∪ 空类型 {
    如果 b ≡ 0 则 返回 皆无
    返回 a / b
}

可选声明槽 — 可选#

当参数或字段可由调用者或构造函数省略时,在名称之后使用 可选:

函数 connect(文本 host, 整数 port 可选) → 无值 {
}

类 User {
    常量 文本 email 可选
}

借用标记可与可选参数组合使用:

函数 process(借自 整数 depth 可选) → 无值 {
}

非空断言 — !#

使用 !., ![, !( 来断言某个可空值不是 空类型:

类 Box {
    常量 整数 ∪ 空类型 val
}

入口 {
    常量 Box ∪ 空类型 maybe_name ← Box { val = 7 }
    常量 _ name ← maybe_name!.val
}

对 空类型 进行非空断言会在运行时中止。

空值合并 — vel#

入口 {
    常量 文本 ∪ 空类型 provided ← 皆无
    常量 _ name ← provided 兜底 "default"
}

未知#

未知 是用于应急通道和不完整知识的顶层未知类型。它不是一种可空性机制。

Conversion and construction#

两个重要的转换运算符,一个用于运行时,一个用于编译时:

# runtime 变换
入口 {
    常量 _ parsed ← "42" ↦ 整数 ⊥ 0
    # static ascription
    常量 整数 value ← 7
    常量 _ text ← value ∷ 文本
}

运行时转换 — ↦#

使用 ↦ 进行运行时转换,尤其是可能失败的解析或强制转换。用 ⊥ 提供内联恢复:

入口 {
    常量 文本 input ← "9"
    常量 _ n ← "42" ↦ 整数 ⊥ 0
    常量 _ safe ← input ↦ 整数 ⊥ 0
}

类型驱动的具象化:

入口 {
    常量 文本 path ← "/etc/hosts"
    常量 _ lanes ← [1.0, 2.0, 3.0, 4.0] ↦ 向量<f32, 4>
    常量 _ body ← 调用 'solum:lege' (path) ↦ 文本
}

静态标注 — ∷#

使用 ∷ 进行显式静态类型标注。它是后置的,并且由目标类型驱动:

入口 {
    常量 整数 value ← 7
    常量 _ x ← 7 ∷ i32
    常量 _ text ← value ∷ 文本
}

空值合并 — vel#

当值为 空类型 时,使用 兜底 进行空值合并:

入口 {
    常量 文本 ∪ 空类型 provided_name ← 皆无
    常量 _ name ← provided_name 兜底 "default"
}