我将如何编写通用函数来处理 ReScript 中的多种记录类型?

How would I write a generic function to handle multiple record types in ReScript?

给定以下人为设计的示例,是否可以编写一个 get 函数来处理具有 a 属性 的任何记录?

type type_one = {a: int}
type type_two = {a: int, b: int}

let example_one = {a: 1}
let example_two = {a: 1, b: 2}

let get = record => record.a

Js.log(get(example_one)) // notice the error here
Js.log(get(example_two))

ReScript Playground

如果不是,这可以用一个对象吗?或者,处理这种情况的最佳方法是什么?

不是。因为记录在名义上(与结构上相反)是类型化的,所以无法指定“具有 a 字段的任何记录”。因此,get 将被推断为具有编译器看到的带有 a 字段的最后类型,即 type_two.

但是有对象类型,它是结构化的子类型,允许这样:

type type_one = {"a": int}
type type_two = {"a": int, "b": int}

let example_one = {"a": 1}
let example_two = {"a": 1, "b": 2}

let get = (record) => record["a"]


Js.log(get(example_one)) // notice no error here
Js.log(get(example_two))

但请注意,使用对象而不是记录需要权衡取舍,例如无法按模式解构它们。

此外,作为旁注,另一种在某些语言中可以实现的方法是通过临时多态性,通过显式定义一个公共接口和附加到特定类型的实现(在 [= 中称为类型 类 28=], Rust 中的特征)。不幸的是,Rescript 和 OCaml 目前也不支持这一点,尽管 OCaml 以模块化隐式的形式提出了建议。但是,您仍然可以使用模块定义通用接口和实现,并 显式 :

传递它们
type type_one = {a: int}
type type_two = {a: int, b: int}

let example_one = {a: 1}
let example_two = {a: 1, b: 2}

module type S = {
  type t
  let getA: t => int
}

module T1 = {
  type t = type_one
  let getA = (record: t) => record.a
}

module T2 = {
  type t = type_two
  let getA = (record: t) => record.a
}

let get = (type a, module(T: S with type t = a), record: a) => T.getA(record)

Js.log(get(module(T1), example_one)) // notice no error here
Js.log(get(module(T2), example_two))

这个用例可能有点冗长,但这有时会派上用场。