鉴于以下人为示例,是否可以编写一个 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))
如果不是,这可以用一个对象吗?或者,处理这种情况的最佳方法是什么?
答案 0 :(得分:3)
不是。因为记录是名义上(而不是结构上)类型的,所以无法指定“具有 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))
但请注意,使用对象而不是记录存在一些权衡,例如无法在模式中解构它们。
另外,作为旁注,在某些语言中可以实现的另一种方法是通过临时多态性,通过显式定义一个公共接口和附加到特定类型的实现(在 Haskell 中称为类型类,在 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))
这个用例可能有点冗长,但这有时确实派上用场。