向 Nickel 添加代数数据类型

  • Nickel 语言:是一种配置语言,也是函数式编程语言。函数式编程定义不明确,从能模糊传递函数作为参数到基于 lambda 演算的静态类型、纯且不可变语言都算。随机问开发者,总会提到代数数据类型(ADTs)和模式匹配,它们是类型化函数式语言的核心。

    • 核心语言特性:let 绑定、头等函数、记录(JSON 对象)、静态类型(默认动态类型,静态类型注解可进行静态类型检查)、合约(类似类型,运行时评估)。
    • 生命周期:编写、评估、序列化(通常为 JSON、YAML 或 TOML),设定每个原生数据结构应能简单直接序列化到 JSON 的准则,有枚举类型(类似 C 或 JavaScript 的标签,序列化后为字符串)和match(类似 C 或 JavaScript 的switch)。
  • ADTs 在配置语言中的情况

    • 序列化难题:希望值能直接序列化为 JSON 数据模型,但对于简单 ADT 的序列化方式难以提前确定,导致考虑使用(非标记)联合类型,其优势是不做序列化选择,而是作为新类型(和合约)对已有可表示值进行分类。
    • 联合类型之路:联合类型是接受不同替代的类型,用\/类型组合符表示,如{literal: Number \/ String},在 TypeScript 中可用于模拟 ADTs,但 Nickel 中联合和交集合约实现困难,限制了用联合类型表示 ADTs 的想法。
  • ADTs 的用途:在编写更多 Nickel 代码后,意识到在库函数中缺少 ADTs,如enum Option<T> { Some(T), None }Result<T,E> = { Ok(T), Error(E) },有多个使用 ADTs 使库更友好的示例,如std.string.find函数可返回适当的 ADT 以改善接口,在合约定义中使用Result<T,E>可使自定义合约接口更简单自然。
  • 设计

    • 结构与名义:传统 ADTs 是名义形式,Nickel 目前的类型系统是结构的,更适合处理任意 JSON 样数据,对于 ADTs 来说结构形式更合适,OCaml 有结构 ADTs(多态变体)。
    • 语法:C 风格枚举是 ADTs 的特殊情况,ADTs 用枚举加参数表示,如'Some "hello"'Ok不是函数应用而是 ADT 构造函数应用,限制 ADTs 为单个参数,可用记录模拟多个参数,ADTs 还带有模式匹配。
    • 类型检查:结构 ADTs 的类型检查与名义 ADTs 不同,引入子类型可解决类型不匹配问题,但子类型有缺陷,Nickel 依赖行多态,可对枚举类型的子序列进行抽象,如forall a. [| 'Ok Number; a |],通过行多态可推断'Ok 42的类型。
  • 结论:由于配置语言设计的特殊性,Nickel 最近才引入 ADTs,探索联合类型无果后,确定了结构版本的 ADTs,它是语言的自然扩展,已在编写更清晰简洁代码和改善库接口方面发挥作用。
阅读 13
0 条评论