渲染zh-Hant

Types and values

Data types#

Faber 採用靜態、型別優先的型別系統。每個宣告都將型別置於名稱之前:文字 名稱,而非 名稱: 文字。型別系統涵蓋純量原始型別、泛型集合、定寬數值、張量,以及面向 GPU 的暫存器型別。

原始型別#

型別作用字面值範例
文字Unicode 字串"Salve, munde"
ascii固定機器權杖'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]>
}

Cursors — 延遲串流#

迭代器<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 使用分隔符語義——每種引號形式都代表不同的原始碼形狀。它們不是可互換的同義詞。

字面形式#

形式類型作用
'…'ascii固定機器標記;不含 §;不含 (…)
"…"文字短 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 ∷ 文字
}

空值合併 — 或取#

當值為 無 時,使用 或取 進行空值合併:

入口 {
    定值 文字 ∪ 無 provided_name ← 可空
    定值 _ name ← provided_name 或取 "default"
}