构造+视图模式实例的实现

Implementation of construction + view pattern example

正在研究作者的 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.tUser.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};
};

但是出现错误,看起来无法区分 viewt

  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};
};