object - 在可扩展对象类型的情况下,我应该如何指向记录字段?

标签 object ocaml reason lenses

想办法在 Reason 中定义一个物体上的简单镜头。

我尝试使用以下代码使用可扩展对象(带有 .. 前置到字段列表):

type hasName('a, 't) = {.. name: 't} as 'a;
type lens('s, 'v) = {
  get: 's => 'v,
  set: ('s, 'v) => 's,
};
let nameLens: lens(hasName('a, 't), 't) = {
  get: s => s.name,
  set: (s, v) => {...s, name: v},
}

我收到“找不到记录字段名称”。错误,尽管 hasName 的类型绝对应该有一个......我在这里做错了什么?

免责声明:我对 Reason/OCaml 真的很陌生,所以我可能会错过一些明显的东西。

最佳答案

错误信息不是很好(委婉地说),但如果您仔细阅读,它确实指出了问题所在。它说“找不到 记录字段 名称。”,考虑到您拥有的是对象而不是记录,这并不奇怪。这是一个错误,因为对象成员是通过 # 访问的。 ,不是 . (见 the Reason docs on objects)。

据我了解,这样做的原因是 OCaml 不支持临时多态性(即函数或运算符重载),因此为了使类型推断正常工作,它需要在语法上区分不同类型的操作。我还怀疑它不只是说“老兄,这是一个对象,而不是记录”的原因是类型系统没有任何记录类型的概念,而是需要特定的记录类型。因此,它首先尝试查找该字段,然后识别与其关联的特定记录类型。如果找不到该字段,则不会将其视为类型错误,因为没有可与之冲突的正确类型。

在任何情况下,使用 # 就可以很容易地解决 setter/getter 问题。而不是 . .然而,二传手问题更大。在这里,您也使用了记录更新语法,并且会得到类似的错误,但不幸的是,对象没有直接的等价物。

使对象面向对象的部分原因是它们的实现是隐藏的。如果你不知道里面是什么,你就不能从外面复制一个对象。虽然您可以创建符合相同对象类型的不同对象,但只有在知 Prop 体类型的情况下才能这样做。在这种情况下你没有。

因此,不可变地更新对象的方法是从对象内部,在 setter 方法中进行,在那里您知道需要知道的一切。不可变地更新当前对象的语法是 {<name: newName>} ,仅在方法定义中有效。然后当然我们还需要将这个setter方法添加到hasName类型。

把这一切放在一起,我们最终得到这个:

type hasName('a, 't) =
  {
    ..
    name: 't,
    setName: 't => 'a,
  } as 'a;

type lens('s, 'v) = {
  get: 's => 'v,
  set: ('s, 'v) => 's,
};

let nameLens: lens(hasName('a, 't), 't) = {
  get: s => s#name,
  set: (s, v) => s#setName(v),
};

let obj = {
  val name =
    "myName";

  pub name =
    name;

  pub setName = newName =>
    {<name: newName>}
};

nameLens.set(obj, "newName")
  |> nameLens.get
  |> print_endline

关于object - 在可扩展对象类型的情况下,我应该如何指向记录字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56187948/

相关文章:

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

javascript - 如何从 onclick 字符串引用 javascript 对象函数

javascript 对象.. 将可能的条目限制为两个?

java - 是否可以根据索引来识别对象?

Java 对象值数组

mysql - OCaml - ocaml-mysql 的时间戳字段异常

functional-programming - OCaml while true do 循环

OCaml 类似于 Python 的 String join 方法

javascript - 将记录列表导出到不带标签的 JavaScript 对象数组