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

时间:2019-02-21 00:36:35

标签: design-patterns ocaml typeerror record reason

正在研究作者的一个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相同的字段或字段子集?如果记录具有不同的字段,或者如果我按模块将记录分开但对特定用例感到好奇,则可以使其工作。

1 个答案:

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