关于编译、重新编译和编译时求值的粗略调查

这是关于zest设计的系列内容的一部分,主要讨论不同语言在编译相关方面的特性,包括:

  • zig

    • 每个类型都有相关命名空间,@import返回的结构体类型在循环导入时没问题,类型在编译时是一等值但运行时不可引用,声明被包装在懒值中,仅在编译时或可达时才强制求值,绑定在解析时急切进行,声明可相互递归,编译时求值确定且无副作用,模拟目标平台实现自然跨编译,编译时和运行时的行为因阶段不同而有差异,过量编译时计算会被检测到。
  • rust

    • 模块完全是二等的,不能直接引用,同一 crate 中不同模块可循环依赖,编译时有限子集语言可用于定义常量和类型参数,所有常量严格求值,静态检测循环,编译时常量类型检查,长运行的常量函数会被检测到,错误可降级为警告。
  • julia

    • 模块用module...end声明,模块系统与文件无关,模块有身份,类型不能在同一模块内重定义,工具可同步源代码和运行代码但不能重定义类型,函数基于“世界年龄”专门化,顶级语句可有任意副作用,jit 时间的代码可有任意副作用,无明确的阶段分离和确定性编译保证,类型可参数化但只能是“纯数据”值,无评估时间的限制。
  • clojure

    • 顶级表达式可有任意副作用,加载顺序易观察,早期绑定但绑定到可变Var,可获取Var的直接引用,循环引用通过前向声明处理,重新加载命名空间会修改现有Var而不是替换,命名空间是一等的但值只能间接获取,无评估时间的限制。
  • erlang

    • 模块不是一等的,通常用符号表示,运行时存储每个模块的最后两个版本,直接函数调用使用调用函数的版本,间接函数调用使用最新版本,闭包不更新函数版本,升级模块需小心规划,无编译时求值,aot 编译较直接,加载顺序可通过on_load观察。
  • unison

    • 顶级声明可包含任意纯表达式,非纯表达式无法类型检查,声明似乎是惰性求值,命名空间不太存在,代码以内容寻址的值/函数存储,版本控制内置于语言中。
  • terra

    • 类似 zig 的阶段划分,用 lua 作为编译时语言,lua 是后期绑定但 terra 是早期绑定,编译 terra 函数时进行 lua 表查找会导致任意副作用,编译不确定且编译顺序可观察。
  • 其他 lisps

    • 其他 lisp 继承 clojure 的模型,术语按顺序逐个求值可导致任意副作用,aot 编译后期添加且依赖程序员确保合理行为,Racket 稍有不同,允许编译时任意副作用但可能回滚突变以防止运行时行为依赖编译顺序。
阅读 9
0 条评论