如果对象类型可扩展,我应该如何指向记录字段?

use*_*298 3 ocaml object lenses reason

在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},
}
Run Code Online (Sandbox Code Playgroud)

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

免责声明:我真的是Reason / OCaml的新手,所以我可能会错过一些显而易见的事情。

gle*_*nsl 5

错误消息不是很好(轻率地说),但是如果您仔细阅读,确实会指出问题所在。它说“ 找不到记录字段名称。”考虑到您拥有的是对象而不是记录,这并不奇怪。这是一个错误,因为使用而#不是访问对象成员.(请参见对象的Reason文档)。

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

无论如何,使用#代替可以轻松解决吸气剂.。但是,二传手的问题更大。在这里,您也使用记录更新语法,并且会得到类似的错误,但是不幸的是,对象没有直接的等效项。

使对象面向对象的原因之一是它们的实现是隐藏的。如果您不知道内部内容,则无法从外部复制对象。尽管可以创建符合相同对象类型的其他对象,但是只有知道具体类型后才能这样做。在这种情况下,您不需要。

因此,不变地更新对象的方法是在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
Run Code Online (Sandbox Code Playgroud)

  • 有一点:当出现“找不到记录字段”错误时,记录投影`r。名称尚未输入。类型检查器在尝试键入“ field”之前试图查找所有名为“ field”的记录标签。由于找不到标签,因此引发了错误,但是该错误仍然是类型检查器错误。 (2认同)