正在通过作者的一个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
相同的字段或字段子集?如果记录有不同的字段,或者我按模块分隔记录但对这个特定用例感到好奇,可以使它工作。
记录是名义上的,而不是结构类型。因此,类型看起来相同是不够的,编译器实际上必须推断出确切的类型定义,如果两种类型相同,这当然是不可能的。但即使它们不相同,编译器也会遇到同名的字段,只需要找到它找到的第一个匹配项,这是最后定义的类型。
在你的情况下,这不是外部问题,因为只暴露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};
};