正在研究作者的一个design pattern called Constructor + View示例,该示例通过类型进行了解释,但是在弄清楚实现时遇到了困难。
这是模块签名:
module User : {
type t;
type view = { name: string, age: int };
let make: (~name:string, ~age:int) => option(t);
let view: t => view;
};
因此User.t
是隐藏的,但是您可以通过一个功能对用户记录进行模式匹配
最初,我认为User.t
和User.view
可以具有相同的字段:
module User: {
type t;
type view = { name: string, age: int, };
let make: (~name: string, ~age: int) => option(t);
let view: t => view;
} = {
type t = { name: string, age: int, };
type view = { name: string, age: int, };
let make = (~name, ~age) => Some({name, age});
let view = t => {name: t.name, age: t.age};
};
但是出现了一个似乎无法分辨view
和t
之间的区别的错误:
Values do not match:
let make: (~name: string, ~age: int) => option(view)
is not included in
let make: (~name: string, ~age: int) => option(t)
再尝试了几件事,第一件事只是取出make
并尝试使view
函数正常工作,但存在相同的问题:
module User: {
type t;
type view = { name: string, age: int, };
let view: t => view;
} = {
type t = { name: string, age: int, };
type view = { name: string, age: int, };
let view = t => {name: t.name, age: t.age};
};
有错误:
Values do not match:
let view: view => view
is not included in
let view: t => view
第二种尝试是将view
类型作为字段的子集(这是我希望使用此模式的用例),但这具有与上述相同的错误:
module User: {
type t;
type view = { name: string, age: int, };
let view: t => view;
} = {
type t = { name: string, age: int, email: string };
type view = { name: string, age: int, };
let view = t => {name: t.name, age: t.age};
};
我的问题是,是否有一种方法可以实现一些适合第一个模块签名的方法,其中User.view
是与User.t
相同的字段或字段子集?如果记录具有不同的字段,或者如果我按模块将记录分开但对特定用例感到好奇,则可以使其工作。
答案 0 :(得分:2)
记录是名义记录,而不是结构类型。因此,仅使类型看起来相同是不够的,编译器实际上必须推断出确切的类型定义,如果两个类型相同,那么这当然是不可能的。但是,即使它们不相同,编译器也将使用相同名称的字段进行挣扎,仅使用找到的第一个匹配项,即定义的最后一个类型。
对于您而言,这在外部不是问题,因为仅公开view
。但是在内部,您必须使用一些类型注释来帮助编译器。编译:
module User: {
type t;
type view = { name: string, age: int, };
let make: (~name: string, ~age: int) => option(t);
let view: t => view;
} = {
type t = { name: string, age: int, };
type view = { name: string, age: int, };
let make = (~name, ~age) => Some({name, age}: t);
let view = (t: t) => {name: t.name, age: t.age};
};