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的新手,所以我可能会错过一些显而易见的事情。
错误消息不是很好(轻率地说),但是如果您仔细阅读,确实会指出问题所在。它说“ 找不到记录字段名称。”考虑到您拥有的是对象而不是记录,这并不奇怪。这是一个错误,因为使用而#不是访问对象成员.(请参见对象的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)