−18° 夜色将尽

type · match · effect · !io

Dawn

一门小而优雅的函数式语言:不可变数据、代数数据类型与穷尽匹配、写进类型签名的效果。

main.dawn在 Playground 打开 ↗
type Shape =
  | Circle(r: Float)
  | Rect(w: Float, h: Float)

fn area(s: Shape) -> Float =
  match s {
    Circle(r) -> 3.14159 * r * r
    Rect(w, h) -> w * h
  }

pub fn main() -> Unit !io =
  [Circle(1.0), Rect(2.0, 3.0)]
  |> map(area)
  |> fold(0.0, (a, x) => a + x)
  |> t => println("total: ${t}")
dawn run main.dawn
total: 9.14159

−12° 海天初分 · 三个概念

一看签名就知道。

函数碰了什么、提前算了什么、匹配了什么,都由编译器检查。

01 · effect

效果进类型

默认是纯的:碰 IO 要标 !io,自己声明的效果由 with handle 应答。

effect Ask {
  fn ask() -> Int
}

fn total() -> Int !Ask =
  ask() + ask()

pub fn main() -> Unit !io = {
  with handle Ask { ask() => 21 }
  println("${total()}")
}
42
02 · comptime

编译期求值

comptime { ... } 在编译期运行普通函数,结果烧进常量池。

fn fib(n: Int) -> Int =
  if n < 2 { n }
  else { fib(n - 1) + fib(n - 2) }

const FIB10: Int =
  comptime { fib(10) }

pub fn main() -> Unit !io =
  println("fib(10) = ${FIB10}")
fib(10) = 55
03 · match

数据与穷尽匹配

不可变的代数数据;漏了一种情况的 match 编译不过。

type Shape =
  | Circle(r: Float)
  | Rect(w: Float, h: Float)
  | Point

fn describe(s: Shape) -> String =
  match s {
    Circle(r) -> "circle ${r}"
    Rect(w, h) -> "rect ${w} x ${h}"
    Point -> "a point"
  }

pub fn main() -> Unit !io = {
  let xs = [Circle(1.0), Point]
  for s in xs {
    println(describe(s))
  }
}
circle 1.0 a point

−6° 天色渐明 · 两个后端

两条路,一个答案。

一份源码,两个后端,一个答案 main.dawn 一路编译成 JVM 字节码,另一路编译成 C 交给 cc。两个程序都运行,stdout、stderr 与退出码必须逐字节相同;有差异就是红灯。 main.dawn JVM bytecode 跑在 JDK 21 上 C → cc 一个原生二进制 out err exit out err exit stdout · stderr · 退出码,逐字节 有差异就是红灯

JVM 字节码与 C 是平级的两个后端。每次 push,差分语料在两边各跑一遍,输出有一处不同就构建失败。

176差分语料里的程序数,每个都在两个后端上各跑一遍
每次 pushstdout、stderr 或退出码有一处不同就是红灯
116,129自举编译器的 Dawn 源码行数

−3° 破晓 · GPU

kernel 也是 Dawn 函数。

cuTile kernel 是一个带具名效果的普通 Dawn 函数,降为 CUDA Tile IR。纯的假设备在没有 GPU 的机器上给出同一个答案。

102/105 个公开 Tile IR opcode 已实现,每个都在 GPU 上与宿主参考对拍过。 覆盖表、门禁与设备台账都在 cuTile 页上 →

设备
  1. vadd一个 Dawn 函数, !Dev
  2. record记下它发出的操作
  3. Tile IR文本或字节码
  4. tileiras汇编成 cubin
宿主
  1. run一个 Dawn 函数, !Gpu
  2. launch("vadd")由 handler 应答
  3. with_gpu_realCUDA driver,仅 nativewith_gpu_fake宿主参考实现,纯的
fn vadd(a: Param[Float], b: Param[Float], out: Param[Float]) -> Unit !Dev = {
  let ta = load_cell(a)
  let tb = load_cell(b)
  store_cell(out, add(ta, tb))
}
dawn run 打印的 Tile IR 里的那次加法%19 = addf %12, %17 rounding<nearest_even> : tile<128xf64>
设备 · !Dev

kernel

运行它会记下它发出的操作;记录变成 Tile IR,再由 tileiras 汇编成 cubin。

  • 在真硬件上核对tile-gpu-diff 把每个 kernel 与宿主参考对拍;台账没覆盖到的改动 CI 会拦下。
fn run(xs: List[Float], ys: List[Float]) -> Result[List[Float], ForeignError] !Gpu = {
  let n = len(xs)
  let a = alloc(F64, n)?
  let b = alloc(F64, n)?
  let out = alloc(F64, n)?
  copy_from_host(a, xs)?
  copy_from_host(b, ys)?
  launch("vadd", blocks(n), [erase(buffer_arg(a)), erase(buffer_arg(b)), erase(buffer_arg(out))])?
  sync()?
  let got = copy_to_host(out)?
  free(a)?
  free(b)?
  free(out)?
  Ok(got)
}
同一次 dawn run 在 with_gpu_fake 下打印的那一行vadd on the fake device: [11.0, 22.0, 33.0, 44.0]
宿主 · !Gpu

宿主程序

with_gpu_real 由 CUDA driver 应答,with_gpu_fake 由宿主内存应答。

  • 只有 native 能驱动 GPU只有 native 能调用 libcuda;在 JVM 上,真设备的 launch 会被拒绝。
  • 假设备是纯的所以 !Gpu 程序能在测试里跑,两个后端都行,不需要 GPU。

0° 日出 · 安装

挑一条路,跑起来。

每个 release 都带两套工具链,各附 SHA-256。

不需要 JVM

dawnc-linux-x86_64

  • 静态链接
  • C 后端
  • 内置 std
base=https://github.com/dawnop/dawn-lang/releases/latest/download
curl -fsSLO $base/dawnc-linux-x86_64
curl -fsSLO $base/dawnc-linux-x86_64.sha256
sha256sum -c dawnc-linux-x86_64.sha256
chmod +x dawnc-linux-x86_64
sudo mv dawnc-linux-x86_64 /usr/local/bin/dawnc

printf 'pub fn main() -> Unit !io = println("hello, dawn")\n' \
  > hello.dawn
dawnc run hello.dawn
hello, dawn

用 JDK 21

dawn-selfhost.jar

  • 任意平台
  • JVM 后端
  • 内置 std
base=https://github.com/dawnop/dawn-lang/releases/latest/download
curl -fsSLO $base/dawn-selfhost.jar
curl -fsSLO $base/dawn-selfhost.jar.sha256
# macOS: shasum -a 256 -c
sha256sum -c dawn-selfhost.jar.sha256

printf 'pub fn main() -> Unit !io = println("hello, dawn")\n' \
  > hello.dawn
java -jar dawn-selfhost.jar run hello.dawn
hello, dawn

最新 release · 教程第 1 章 · 规范 · 示例