如何在 rescript 中将类型构造函数设为私有(当前模块除外)?

How to make a type constructor private in rescript (except in current module)?

我想创建一个验证函数,它取一个名字并输出一个 validName 类型。我不希望在不使用函数 validateName.

的情况下在模块外部构造类型 ValidName 的值

我正在尝试将 ValidName 类型设为私有,但这使我无法在 validateName 函数中使用它(如果它在同一模块中则为事件)。

在 rescript 中执行此操作的正确方法是什么?

Here is a playground

代码如下:

module MyModule = {
    type notValidatedName = NotValidatedName(string)
    type validName = private ValidName(string)
    type errorMessage = string
  
    let validateName = (~name: notValidatedName): Belt.Result.t<validName, errorMessage> => {
      switch name {
      | NotValidatedName(name) when Js.String2.length(name) <= 2 => Belt.Result.Ok(ValidName(name)) // not possible because ValidName is private
      | _ => Belt.Result.Error("String is too short to be a name")
      }
    }
}

let nameToShort = MyModule.ValidName("aa")           // I don't want this to be possible
let notValidName = MyModule.NotValidatedName("aa")   // This is fine

let nameResult = MyModule.validateName(~name=notValidName)

可见性是在模块签名中指定的(通常是类型注释),而不是定义本身。您也不需要构造函数,但应该将类型设为抽象类型或将类型别名设为私有。

您可以在本地模块上指定模块签名,如下所示,但通常您会将其放在 .resi(“接口”)文件中。您可以放入模块签名中的所有内容也可以放入接口文件中。有关更多信息,请参阅 the docs

我会这样做:

module MyModule: {
  type validName = private string
  let validateName: string => result<string, string>
} = {
  type validName = string

  let validateName = name => {
    if String.length(name) <= 2 {
      Ok(name)
    } else {
      Error("String is too short to be a name")
    }
  }
}

let nameToShort: MyModule.validName = "aa" // Type error: is 'string', wanted 'MyModule.validName'
let notValidName: string = "aa" // This is fine

let nameResult = MyModule.validateName(notValidName)

switch nameResult {
  | Ok("aa") => Js.log("yay!")
  | Ok(_) => Js.log("wat?")
  | Error(err) => Js.log(err)
}

私有类型别名意味着该类型的值在任何方面都被视为 string,除了您不能在定义它的模块之外构造或强制该类型的字符串。

如果您将其抽象化,即通过将 type validName = private string 替换为 type validName 来实现,则您可以完全隐藏别名,不让外界看到。与这种类型的值交互的唯一方法是通过它公开的函数将它传回模块。