Dawn 设计文档

English —— 本文是正本;英文是它的译本,scripts/doc-check.py 盯着两者不脱节。

状态:historical。本文是 M0–M4 时期的决策记录,逐条保留是为了「当时为什么这么选」 有据可查。其中若干前提已被后续里程碑推翻,最要紧的三条:

  • 「编译器预算 6–8 千行(Kotlin)」「实现语言 = Kotlin」——Kotlin 实现已随 kotlin-final tag 归档,main 上只有自举的 selfhost/(约 3.5 万行 Dawn), 见 m8-selfhost-only.md
  • 「unsafe 逃生门不向用户代码开放」——unsafe_pure 现在是普通语法,用户可写, 见 spec.md §6.4 与 codebase-audit.md 的 LANG-01。
  • 「不引入 IR」——论证在小编译器阶段成立,现在 checker + emit 已过一万行, 见 codebase-audit.md 的 ARCH-04。

语法与语义的权威定义在 spec.md(那篇是 normative)。本文只讲"为什么"。

1. 目标

2. 非目标(v0.1 明确不做)

不做理由
~~完整代数效应(handlers)~~ → 非尾恢复档(多次恢复 / 延续捕获)~~工程黑洞~~ → 尾恢复档已落地(effect + with handle,spec §6.5);剩下的那档要延续捕获,与 C 后端的 Perceus + 单栈模型正面冲突
~~trait / typeclass~~~~先用显式传函数顶着,疼了再加~~ → 疼了,2026-07-13 已加(见 D9)
async / 协程!io + JVM 虚拟线程顶着,语言层不引入着色问题
comptime 覆盖主要场景,且不引入第二门语言
自定义运算符防生态长成早期 Scala
Java → Dawn 方向互操作双向 ABI 稳定是大语言的活
REPL与 comptime/整体编译模型冲突,性价比低
自定义 GC / 内存模型JVM 的 GC 就是我们的 GC

3. 决策记录

D1 唯一后端 = JVM 字节码(ASM 直出)

native-image 吃字节码,故 dawn build --native 只是在 jar 后面接一条 native-image 命令。代价是接受封闭世界假设,因此语言设计上主动规避其雷区:

D2 效果系统只有两级:pure 与 io

完整的 Flix 式多态 effect 系统很漂亮,但类型推导、错误信息、教学成本全部翻倍。 Dawn 的格子只有两个点 {pure, io},外加效果变量做最简效果多态 (map(f) 的效果 = f 的效果)。实现上这只是类型检查器里多传一个布尔格子; 收益却是大头:纯函数可以被 comptime 调用、可以被激进优化、测试无需 mock。

升级路径:若未来要加 !net / !fs 等细分效果,格子从两点扩成幂集, 语法位置(! 后缀)已经预留。

D3 comptime 而非宏;comptime 不操作类型

编译器必须有常量折叠 → 必然内含一个 AST 解释器 → 把它暴露给用户就是 comptime。 只允许纯代码,产物必须是常量可序列化值。刻意不做 Zig 那种"类型是一等 comptime 值"——那会把类型检查器和求值器搅成一团,是小实现驾驭不住的复杂度来源。 泛型走独立的、无聊的参数多态(擦除 + 装箱),不与 comptime 耦合。

D4 错误处理 = Result + ?,panic 兜底

异常破坏"签名即契约"(一个不标 !io 也能炸的函数是纯度谎言)。 可恢复错误走 Result[T, E] + ? 传播;不可恢复走 panic()(进程终止, JVM 上映射为抛 Error,native 上 abort)。panic 不需要 !io——它不返回。

D5 Java 互操作:单向、全部 !io、null 自动包 Option

D6 无中间 IR

AST → 类型/效果检查(在 AST 上标注)→ ASM 直出字节码。优化交给 JVM JIT 和 native-image。等真的需要优化 pass 或第二后端时再引 IR——为想象中的需求 预付架构成本是小项目最常见的死法。唯一的例外是尾递归重写(AST 层变换)。

D7 实现语言 = Kotlin

比 Java 少三成样板(数据类、sealed class、模式匹配雏形正好用来写编译器), 产物同样是 jar,同样能 native-image 自举编译器本身。

D8 互操作三件套:SAM 转换、数组直通、List 零拷贝桥(2026-07-12 定稿)

M6 前置,先用 playground runner 验收。四个语义决策全部对照过 Kotlin/Scala/Clojure 前例:

D9 trait = 单参数名义 typeclass + 字典传递(2026-07-13 定稿)

完整设计与实现记录见 trait.md;spec §3.5 是规范化摘要。要点:

D10 自举前清障批(P0.6,2026-07-22 定稿)——语义暗角修补与三项「维持现状」

自举后每改一处语义要同时伺候两个编译器,故趁 parser 唯一时把设计审查 (对照 orthogonality / explicit-over-implicit / 语言设计常见错清单)翻出的账一次结清。 已修(各见 spec 对应节与提交记录):数值边缘语义写死为保证(§4.3)、Float/Bytes 禁作 Map/Set 键(§2.2)、alias 独立关键字(§2.6)、字段调用同名冲突改报错(§2.4)、 break/continue(§4.7)、行首 . 续行(§1.7)、Cursor 不透明类型(§11)。 审查后维持现状的三项,理由记档以免反复:

另:审查曾疑「顶层声明静默遮蔽 prelude」——P0.6 时的实况是顶层重定义 builtin/std 名 报错。P0.7 已按 `stdlib-naming.md` 把顶层 fn / trait 方法遮蔽 builtin/std 函数名改为 Rust 式合法(解析序本就是本模块声明 → std → 内建,只删了 注册期报错;std 模块自身的 pub fn len 是第一批受益者)。内建/prelude 类型与 trait 名(Map/Option/Ord…)仍不可重定义;spec §10.3 是权威表述。

4. 特性来源致谢

5. 里程碑

两条横向轨道贯穿全程:LSP 随每个特性同步演进(M1 已验证节奏可行; 2026-07-12 补齐 completionProvider——作用域符号/内建/构造器/关键字 + 字符串注释 抑制 + ! 触发 io,web 框架同刀撞出的 G1 字段直调/G2 类型别名/G3 刚性类型 参数三处语言缺口同日落地,spec §2.4/§2.6/§2.5); 验收样例先行——每个里程碑动工前先把验收物写好放进仓库。

trait v1(2026-07-13,六刀落地,见 D9 与 trait.md):单参数 typeclass + 字典传递;trait/impl/[T: Ord] 语法、一致性 + 孤儿规则、 < <= > >= 桥接预置 Ordderive Ordsort/sort_by/max/min/ max_by/min_by;验收样例 examples/traits.dawn,教程 §15,spec §3.5; 测试 1094+ 项全绿,JVM/native 对拍一致。

人体工学四件套(2026-07-12,对 Kotlin/Rust/Scala 等 []/推断/局部函数惯例 调研后定案,测试 973 项全绿):① [] 下标——List/Map 断言语义(越界/缺键 panic),get/map_get 保持 Option 问询,comptime 支持 List 下标(spec §4.8); ② return 提前返回——Never 类型表达式,作用域规则同 ?(spec §4.9);③ 局部 命名函数——块内 fn name(...),可捕获/可递归/自尾调用成环,递归调用编译为 impl 方法 invokestatic 直调、自引用作值走闭包重建(spec §3.1、§12.4);④ 私有 函数签名推断——非 pub 可省返回类型与效果,按调用图拓扑序推导,递归/?/return 须标注;const 检查拆两段(先登记类型后查初始化器)以便 const 调用推断函数 (spec §3.1)。web 框架/站点生成器同日 dogfood 换用 []