D0006: Type System

Notist 最终需要完整的静态类型系统,同时保留运行时 Value。静态类型负责在执行前验证表达式和函数调用;Value 负责承载已经求值的数据,并交给 native、user 或 plugin function 执行。

Function API 使用结构化参数,避免每个函数自行解释字符串;类型能力可以逐步扩展,但不会改变 Type、Value 与 binding 的分工。

Functions Produce Content

heading、raw、quote 等“processor”本质上都是 callable function:

#heading(level=1)[Introduction]
#raw(text=`fn main() {}`, lang="rust")
#quote[quoted *content*]

它们可以具有普通函数签名:

heading(level: Int = 1, body: Content) -> Content
raw(text: String, lang: String? = none) -> Content
quote(attribution: Content? = none, body: Content) -> Content

因此不需要长期维护独立的 Processor type 和 ProcessorRegistry。统一系统包含 Function、FunctionRegistry、FunctionInput、FunctionOutput 和 FunctionContext。

Core Types

核心类型集合包括:

  • None

  • Bool

  • Int

  • Float

  • String

  • Content

  • Array<T>

  • Dict<K, V> 或结构化 record

  • Function

Content 是可组合的语义 Element sequence,不是普通字符串。Raw source 由 host syntax 产生 String,并通过独立 source range 保留诊断映射;String 只有在显式插入 Content 时才转换为 Text。

类型还需要可选类型、联合类型或等价能力,以表达 String?none 和未来更精确的参数约束。

Runtime Value

即使拥有静态类型,运行时仍需要 Value:

Value
├─ None
├─ Bool(bool)
├─ Int(i64)
├─ Float(f64)
├─ String(String)
├─ Content(Content)
├─ Array(Vec<Value>)
├─ Dict(...)
└─ Function(...)

Type 描述表达式“可以产生什么”,Value 表示本次执行“实际产生了什么”。常量求值、默认参数、用户函数和控制流都需要 Value。

Expressions and Binding

函数参数的最终流程:

source arguments
-> Expression AST
-> name resolution
-> type inference/checking
-> evaluation to Value
-> signature binding
-> Function call

例如:

#heading(level=1 + depth)[Title]

parser 产生 Expression AST;analysis 解析 depth;type checker 确认表达式为 Int;eval 得到具体 Value;binder 将它绑定到 level

Function Signature 至少描述:

  • 参数名称与顺序。

  • positional/named 调用规则。

  • 参数类型。

  • required/optional/default。

  • trailing body 类型。

  • 返回类型。

完成 binding 后,native function 不再读取参数源码字符串。

User-Defined Functions

统一 Function 模型后,可以自然支持 Typst 风格的用户函数。建议先采用显式类型:

#let add(a: Int, b: Int) -> Int = a + b

#let warning(
    title: String = "Warning",
    body: Content,
) -> Content = #quote[
    #heading(level=3)[#title]
    #body
]

Function 可以有三种实现来源:

  • Native Function:由 Notist 核心实现。

  • User Function:由 Notist 源码定义。

  • Plugin Function:由扩展提供。

三者共享名称解析、signature、参数 binding、Value 和返回类型检查。差异只在执行后端与权限边界。

Syntax Sugar

语法糖由 parser 直接识别,但其语义应归一到内置 Element 构造器:

= Title
-> builtin heading constructor

`raw`
-> builtin raw constructor

语法糖不能通过动态名称查找改变含义。即使用户定义同名 heading= Title 仍表示核心 Heading。显式 #heading[...] 是否允许局部遮蔽,则由名称解析规则决定。

插件可以注册函数并使用通用 Call 语法,但不能直接修改核心 grammar。未来的用户宏或声明式 sugar 应作为独立系统设计。

Analysis and Evaluation

静态 analysis 负责:

  • 建立 module 和 lexical scope。

  • 名称解析与 symbol identity。

  • 推断或检查 Expression 类型。

  • 解析 Function Signature。

  • 校验参数、trailing Content 和返回类型。

  • 产生不执行用户代码的 diagnostics。

Evaluation 负责:

  • 求值 Expression AST 为 Value。

  • 应用默认参数并完成运行时 binding。

  • 执行 Native/User/Plugin Function。

  • 产生 Content 和运行时 diagnostics。

Type checking 不执行 Function。这样 LSP 可以快速、确定地分析未保存或不完整的文档。