Lamina 是静态强类型、表达式导向的数学 DSL/脚本语言,支持单位系统、代数表达和局部类型推导。
let, var, const, unit, func, return, in, not, loop, while, break, continue, if, else, match, sym, import, use, as, and, or
- 算术运算符:
+,-,*,/,%,^ - 优先级(高到低):
^*,/,%+,-
- 标量比较:
==,!=,<,<=,>,>= - 广播比较:
.==,.!=,.<,.<=,.>,.>= - 逻辑:
and,or,not - 规则:逻辑运算优先级低于算术运算
- 规则:未加点的比较仅用于标量;
vector/matrix比较必须使用加点算子 - 优先级(高到低, 低于3.1运算符):
or,and==,!=>,<,>=,<=
- 赋值:
= - 绑定:
let(只读),var(可变) - 符号声明:
sym(类型固定为Expr) - 优先级:最低
- 线性代数:
*,\\,/,^ - 元素广播:
.*,./,.+,.-,.^ - 关系广播:
.==,.!=,.<,.<=,.>,.>= - 转置:
'(共轭转置),.'(非共轭转置) - 优先级: 线性代数暂定,元素广播同3.1,关系广播同3.2
- 从属:
in,not in - 子集:
subset - 并交差:
|,&,- - 对称差:
xor
- Lambda 箭头:
-> - match 分支箭头:
=> - 管道:
|>
| 运算符 | 仅适用类型 | 语义 | 歧义处理 |
|---|---|---|---|
/ |
num |
数值除法 | 若操作数不满足数值或矩阵规则,编译报错 |
\\ |
matrix |
矩阵左除(解 A X = B) |
左值必须可作为系数矩阵,维度不匹配或系统无解时报错 |
/ |
matrix |
矩阵右除(解线性系统) | 左值必须是矩阵,右值可用于右除,否则报错 |
^ |
num / Expr |
幂运算 | 集合对称差使用 xor |
^ |
matrix |
矩阵幂(整数指数) | 非方阵或非整数指数报错 |
xor |
set |
集合对称差 | 集合对称差统一使用 xor |
< <= > >= == != |
标量(num/complex/text/bool) |
标量关系比较 | 操作数为 vector/matrix 时编译报错,要求使用加点比较 |
.* ./ .+ .- .^ |
vector/matrix 与标量或同形状容器 |
元素级广播 / 哈达玛运算 | 维度不兼容报错 |
.== .!= .< .<= .> .>= |
vector/matrix 与标量或同形状容器 |
元素级关系广播 | 返回同形状 vector<bool>/matrix<bool>,维度不兼容报错 |
具备自底向上类型推导,多数情况推荐手动标注类型
- 允许推导:局部
let/var绑定、for 推导式局部变量 - 必须显式:函数参数类型、函数返回类型、模块导出符号类型
- Lambda 与 match 的推导规则由 LSR-006 与 LSR-005 定义
- 推导结果必须为确定类型;约束不足时编译报错
变量声明统一 let|var|const name type = expr 或者 let|var|const name = expr。
let:只读绑定, 当绑定为对象类型时,仅代表不可再次绑定另一个对象|值,而不是对象本身不可变var:可变绑定const:编译期常量声明(只读,要求初始化表达式可在编译期求值)
let a = 10
let pi_val real = 3.1415
let fraction = 3/2
const TAU = 2 * pi
let c = 5/4
let name = "LSR"
var counter = 0
counter = counter + 1
a = 20 # 编译报错
| 类型 | 说明 | 字面量 / 示例 |
|---|---|---|
num |
数值超集(int, real, frac) |
42, 114.514, 1/2 |
null |
空类型,既是类型也是值 | |
complex |
复数 | 3 + 4i |
text |
文本 | "i love lamina" |
bool |
布尔逻辑 | true, false |
set |
无序去重集合 | {1,2,3} |
vector |
一维同构数值数组(1 索引) | vec[1,2,3] |
table |
哈希表(任意可哈希键) | 见 4.5 |
matrix |
二维同构数值网络(1 索引) | mat[1,2;3,4] |
Expr |
CAS 符号表达式 | x^2 + 1 |
- 在当前定义中,
int,bool,null为值类型,其他为对象引用类型
table 是键值容器,逻辑类型记作 table<K, V>。
K必须是可哈希类型, 单个表支持多种不同可哈希键类型V可为任意类型(可包含num、text、vector、matrix、Expr等)- 这会是你在LSR中看到的最动态类型的容器
字面量语法使用 table{key => value, ...}:
let empty_tbl = table{}
let scores = table{"alice" => 98, "bob" => 87}
let mixed_val = table{"v" => vec[1,2], "m" => mat[1,2;3,4]}
类型推导规则(暂不做数,待定):
- 空表需在后续写入中确定
K、V,若约束不足则编译报错 - 非空字面量中,所有 key 必须可统一到同一
K - value 统一到同一
V;数值类型允许按数值塔提升
默认可哈希键类型:complex、text。
默认键类型范围仅包含:complex、text、bool。其他类型作为键需包含.hash()子方法。
不支持num键类型的原因: 支持朴素标识符键类型,编译时哈希
let comping_hash_table = table{
animal => "dog", num => "1"
} //这部分通常不可遍历
读取语法:tbl[key]。
let scores = table{"alice" => 98, "bob" => 87}
let a = scores["alice"]
键规则:
- 直接读取
tbl[key]且键不存在时,返回null - 写入键必须可哈希,否则runtime报错
写入语法:tbl[key] = value,包括覆盖与新增。
var scores = table{"alice" => 98}
scores["alice"] = 100 # 覆盖
scores["bob"] = 91 # 新增
scores.has("alice") # bool
scores.keys() # vector<K>
scores.values() # vector<V>
scores.items() # vector<(K, V)>
- 复合类型实现采用共享引用,降低拷贝成本
- 可观察语义采用写时复制(COW)
- 只读操作(读取、遍历、计算)不触发复制
- 一旦发生可变写入(
var绑定上的突变),若对象被共享,必须先分离副本再写入
- 赋值后两个变量指向同一底层对象(
is可能为true) - 对其中一个变量做可变写入时,触发 COW,另一个变量保持原值
var t1 = table{"x" => 1}
var t2 = t1
t2["x"] = 9
# COW 后可观察结果
# t1["x"] == 1
# t2["x"] == 9
- 非值类型参数采用引用传递
- 函数内对局部可变绑定突变时,按 COW 分离
- 修改结果通过
return新对象交付调用方
func bump(src table) -> table {
var t = src
let v = t["n"] # 若为值类型则产生复制,否则为引用
if v != null {
t["n"] = v + 1
}
t
}
var a = table{"n" => 0}
var b = a |> bump()
# a["n"] == 0
# b["n"] == 1
let:只读绑定,COW 写路径仅由var触发var:可变绑定,触发 COW 规则- 本规则统一适用于
vector、matrix、table、set
vector 与 matrix 强齐次,保证类型稳定与 BLAS 友好。
vector<T>要求所有元素类型一致matrix<T>要求所有元素类型一致- 对数值容器,元素量纲签名也必须一致
示例:
let ok_v = vec[1<m>, 2<m>, 3<m>] # 合法:同类型同量纲
# let bad_v = vec[1<m>, 2<s>] # 非法:量纲不一致
异构数据建议:
- 需要混合不同量纲(如
[pos, vel])时,使用table - 或使用后续结构体规范(若引入)
单位通过尖括号 <> 附加于数值后形成复合类型。编译器执行量纲代数(约分、乘除)与加减一致性检查。
let dist = 10<m>
let time = 2<s>
let spd = dist / time
# let error = 5<m> + 2<s> # Dimensional Mismatch
单位转换必须使用 as:
let v = 20<m/s>
let v_kmh = v as <km/h>
用户可使用 unit 声明新单位,支持两种形式:
- 抽象基单位:
unit U - 派生单位:
unit V = expr<...>
unit score
unit token
unit km = 1000<m>
unit min = 60<s>
unit N = 1<kg*m/s^2>
unit level = 100<score>
约束:
- 单位名在当前命名空间内唯一
- 单位定义要求无环(如
unit a = 2<b>且unit b = 3<a>形成环) - 抽象基单位可直接创建;派生单位需可归约到已知单位组合(SI 或抽象基单位)
- 派生单位必须可归约到已知单位组合(SI 或已声明抽象单位)
- 定义成功后可参与
as <...>转换(仅限同量纲)
- 量纲签名在编译期类型检查阶段表示为“SI 七维 + 用户基单位”的稀疏指数映射
- 无量纲纯数字对应零向量
- 乘除运算对应向量加减
- 相同量纲不同缩放单位仅允许通过
as显式转换
实现约束:
- 运行期普通数值不携带量纲元数据
- Codegen 阶段应抹除量纲标签,退化为原生数值(如
float64/int) - 以便复用 BLAS/LAPACK 等高性能线代后端
- 仅
Expr(CAS)允许保留量纲符号,用于符号推导中的一致性验证
带量纲值剥离为纯数值的语法与边界由 LSR-008 定义。
let dist = 10<m>
let raw = dist as num # SI 基准转换后剥离
let raw_keep = dist as scalar # 仅剥离标签,保持当前刻度
let speed = 20<m/s>
let raw_kmh = (speed as <km/h>) as num
规则:
- 默认剥离到
num使用 SI 基准量值 as scalar仅移除量纲标签,不进行数值缩放- 若需要特定单位下的纯数值,先做单位转换再剥离
as num<_>在新规范中非法,必须使用as num
函数签名语法:func name(param Type, ...) -> ReturnType
func kinetic_energy(m num<kg>, v num<m/s>) -> num<J> {
return 1/2 * m * v^2
}
func second2hour(s num<s>) -> num<h> {
return ((s as num) / 3600) as num<h>
}
- 本文件不定义 Lambda 语法与类型推导细则
- Lambda 的语法、类型推导、与管道交互由 LSR-006 单独定义
- match 的语法、穷尽检查、模式系统由 LSR-005 单独定义
if 是表达式,必须有 else(或提前 return 完全覆盖)。
let abs_val = if x < 0 {
-x
} else {
x
}
loop x { ... } 表示循环体执行 x 次。x 必须是可确定为非负整数的表达式。
loop 5 {
print("tick")
}
let n = 3
loop n {
counter = counter + 1
}
规则:
- 循环次数在进入循环前求值一次
- 若
x == 0,循环体不执行 - 若
x < 0或不是整数,编译报错
while cond { ... } 在 cond 为 true 时重复执行循环体。
var i = 0
while i < 5 {
i = i + 1
}
规则:
- 每轮迭代前重新计算
cond cond必须是bool类型,否则编译报错- 支持
break与continue
迭代:for 名字 in 可迭代对象 {...}
不论迭代还是推导式,其可迭代对象必须包含 begin() next(last iterator) 和 end()子方法
let person = vec[2,4,32,5,6]
for num in person {
print(num)
}
# 等价写法:
let person = vec[2,4,32,5,6]
{
let end = person.end()
var iter = person.begin()
var num = iter.get()
while iter != end {
print(num)
iter = person.next(iter)
num = iter.get()
}
}
推导式语法:表达式 for 变量 in 可迭代对象 if 守卫条件
# 向量推导
let squares = vec[x^2 for x in range(1, 10) if x % 2 == 0]
# 集合推导
let abs_set = {abs(x) for x in vec[-1, 1, 2, -2]}
# 矩阵推导
let M = mat[i * j for i in range(1,3); for j in range(1,3)]
规则:
- 推导式内部为独立词法作用域
- 外部变量访问范围:读取外部
let;外部var通过显式返回更新 - 迭代器单次求值
let A = mat[1,2,3;4,5,6;7,8,9]
let v = vec[1,2,3]
let A_double = [A, A]
let A_stack = [A; A]
let element = A[2,3]
let row2 = A[2,*]
let col3 = A[*,3]
let submat = A[1..2,2..3]
- 标准线性代数算子:
*,\\,/,^ - 元素广播算子:
.*,./,.+,.-,.^ - 关系广播算子:
.==,.!=,.<,.<=,.>,.>= \\与/分别定义为左除与右除:A \\ B解A X = B,B / A解X A = B\\、/与^采用按类型分派;无法唯一分派时编译报错- 未加点的关系比较仅用于标量;容器比较必须使用加点算子
let X = A \\ B # 线性系统 A X = B
let Y = B / A # 线性系统 Y A = B
let B = A .+ 10
let C = A .* A
let D = A * A # A 必须为方阵
let flags = vec[1,2,3] .> 1 # vec[false, true, true]
# vec[1,2,3] > 1 # 编译报错(容器比较需使用 .>)
dot(v1, v2) # 点积
cross(v1, v2) # 叉积(3维)
norm(v) # 范数
v1 .+ v2 # 逐元素加法
v .* 2 # 标量广播
matmul(A, v) # 矩阵-向量乘
matmul(A, B) # 矩阵-矩阵乘
inv(A) # 逆矩阵
det(A) # 行列式
| 运算符 | 含义 | 示例 |
|---|---|---|
in / not in |
元素从属 | 2 in {1,2,3} |
subset |
子集关系 | {1,2} subset {1,2,3} |
| ` | , &, -, xor` |
并、交、差、对称差 |
预定义数学常量:pi, e, phi
预定义集合:{R}, {Z}, {Q}, {C}
层级:Z ⊂ Q ⊂ R ⊂ C ⊂ Expr
Int (Z):大整数,与int区别在于其为无限精度Fraction (Q):分数(分子分母均为Int), 与frac区别在于frac分子分母均为intReal (R):实数Complex (C):复数Expr:符号表达式
提升规则:
- 二元运算中,低层级自动提升到高层级
Expr降级需显式转换- 除法只有在显式涉及
Real时返回Real - 数值提升与单位推导互不干涉
let a = 2 + 1/2
let b = 1/2 + 0.5
let c = 2.0 + 3i
sym x
let d = 5/2 + x
Expr 为延迟求值 AST;参与数值运算时整体提升为 Expr。
let c = 5
sym x
let res = c * x
Expr 降级必须显式:
- 代入求值:
expr(symbol => value) - 强制浮点:
evalf(expr)
sym x, y
let equation = x^2 + 1 + y*2
let val1 = equation => (x => 3, y => 4)
let exact = sin(pi / 4)
let approx = evalf(exact)
- 结构相等:
== - 数学等价(仅
Expr):===(由 LSR-007 单独定义)
let a = vec[1,2,3]
let b = a
b is a # true
let c = vec[1,2,3]
c is a # false
vec[1,2,3] == vec[1,2,3] # true
[1,2;3,4] == [1,2;3,4] # true
func square(x num) -> num {
return x^2
}
let nums = range(1,10)
let squares = nums |> map(square)
range(start int, end int) -> vector
import:加载模块并绑定模块对象。
use:从模块导入符号到当前作用域(冲突时报错)。
import std.math
import physics.quantum
let val = math.sin(math.pi / 2)
use std.math.{sin, cos, pi, e}
use std.linalg.{det}
let wave = sin(pi * x) + cos(e * x)
let D = det(matrix_A)
use std.constants.{G as GRAVITY}
LSR-003:Lamina C 扩展与插件规范LSR-004:标准库规范(模块结构、数学系统、库调用约定)LSR-005:match 规范(语法、穷尽检查、模式系统)LSR-006:Lambda 规范(语法、类型推导、管道交互)LSR-007:===数学等价规范(判定边界与化简策略)LSR-008:量纲剥离规范(as num语义、边界与错误模型)