OCaml 有没有办法在记录中声明一个整数,它是一些整数的区间?

Is there a way in OCaml to declare in a record an integer which is an interval of some integers?

我被困在这个问题上,我不得不: 将类型日期定义为三元组,其中第一个组件是表示为 整数,第二个分量是区间 [1..12] 中的整数 代表月份,最后一个组成部分代表一天,有一个整数 数.

type date = { year: int; month: int; day:int};;

{year = 2012; month = 12; day = 21};;

这很好用,但对于参数月,我需要输入 1 到 12 之间的整数。

如果你只想为几个月使用一个变体并枚举整个月。你可以对天数做同样的事情,但我会避免它。

如果你想要一个更一般的概念,比如范围类型:你不想要那个,因为月份有 30 天或 31 天,而二月有 28-29 天,具体取决于闰年,所以它是依赖范围......如果这张支票可用,那么你想要哪个范围?您需要的检查非常具体:例如,我只在带有扩展名的 cpp 中看到过这个,并且您被迫在范围内使用 int (非常无用)。因为这个扩展需要 static int 你不能真正用于日期。 OCaml 无法做到这一点,因为所有 int 都是 int 而不是 30 或 31。通常的解决方法是创建一个函数,该函数采用三个 int 验证它们是否在有效范围内并且 return date option并从外部将其设为私有,这样您就不会破坏不变量。我们称之为智能构造函数。

module SafeDate : sig
    type date = private { year: int; month: int; day:int}
    val create: int -> int -> int -> date option
end = struct
    type date = { year: int; month: int; day:int}
    let create year month day = if (* put formula/code to say if correct *) then Some{year; month; day} else None
end

没有,但是可以防止意外发生。

OCaml(和其他地方)中的时间和日期库通常确保日期对象的组件落在正确的范围内,而无需特殊的类型系统技巧。

在 OCaml 中,一个好的解决方案可能是将日期字段公开为只读。这意味着模块的用户将无法使用符号 {year=...; month=...; day=...} 创建此类记录。这是通过提供一个明确的模块接口(.mli 文件)来实现的,该接口将日期类型声明为 private:

(* Date.mli *)
type t = private {
  year: int;
  month: int;
  day: int;
}

(** Create a date object if and only if given acceptable values *)
val create : year:int -> month:int -> day:int -> t option

实施(Date.ml 文件)与往常一样。特别是,类型定义中没有 private 关键字:

(* Date.ml *)
type t = {
  year: int;
  month: int;
  day: int;
}

let create ...

您也可以借此发挥创意。例如,您可以让库安全地公开从 year/month/day 派生的字段,例如自年初以来的天数等