如何将OCaml整数类型约束为整数范围?

时间:2016-01-30 22:25:13

标签: types ocaml

阅读真实世界OCaml书,我遇到了以下类型声明(第6章:变体):

# type color =
  | Basic of basic_color * weight (* basic colors, regular and bold *)
  | RGB   of int * int * int       (* 6x6x6 color cube *)
  | Gray  of int                   (* 24 grayscale levels *)
;;

type color =
    Basic of basic_color * weight
  | RGB of int * int * int
  | Gray of int

我认为RGB和灰色变体可能会受到进一步限制。例如,RGB元组中的每个int应该只能具有值0-5。

在Erlang中,我这样做:

-type rgbint() :: 0 | 1 | 2 | 3 | 4 | 5.
-type rgb() :: {rgb_int(), rgb_int(), rgb_int().

然而,当我在OCaml(在utop中)尝试这个时,它抱怨道:

# type rgbint = 0 | 1 | 2 | 3 | 4 | 5 ;;

Error: Parse error: [type_kind] expected after "=" (in [opt_eq_pluseq_ctyp]) 

问题:

  • 在OCaml中,是否不允许在类型定义的RHS上使用文字?
  • 如何在OCaml中执行上面的Erlang rgbint()定义?

感谢和祝福

伊万

2 个答案:

答案 0 :(得分:5)

这是私有类型的典型用例

OCaml允许您mark a type as private in a signature 这使它成为具体和抽象类型之间的东西:

  • 类似于具体类型的值,私有类型的值 可以使用匹配模式解构;

  • 类似于抽象类型的值,私有类型的值 只能使用模块中的函数构造 定义这种类型。

例如,您的代码段可以翻译为

module Color : sig
  type t =
  | Basic of basic_color * weight   (* basic colors, regular and bold *)
  | RGB of rgbint * rgbint * rgbint (* 6x6x6 color cube *)
  | Gray of int                     (* 24 grayscale levels *)
  and basic_color =
   | Black | Red | Green | Yellow | Blue | Magenta | Cyan | White
  and weight = Regular | Bold
  and rgbint = private int
  val rgb : int * int * int -> t
end = struct
  type t =
  | Basic of basic_color * weight
  | RGB   of rgbint * rgbint * rgbint
  | Gray  of int
  and basic_color =
   | Black | Red | Green | Yellow | Blue | Magenta | Cyan | White
  and weight = Regular | Bold
  and rgbint = int

  let rgb (r, g, b) =
    let validate x =
      if x >= 0 && x < 6 then x else invalid_arg "Color.rgb"
    in
    RGB (validate r, validate g, validate b)
end

使用此定义,我们当然可以使用Color.RGB函数创建Color.rgb值:

# Color.rgb(0,0,0);;
- : Color.t = Color.RGB (0, 0, 0)

无法从其组件中自组装Color.RGB值:

# Color.RGB(0,0,0);;
Characters 10-11:
  Color.RGB(0,0,0);;
            ^
Error: This expression has type int but an expression was expected of type
         Color.rgbint

可以使用强制类型将类型Color.rgbint的值解构为整数:

# match Color.rgb(0,0,0) with
  | Color.RGB(r,g,b) ->
    if ((r,g,b) :> int * int * int) = (0, 0, 0) then
      "Black"
    else
      "Other"
  | _ -> "Other";;      
- : string = "Black"

Yaron Minsky撰写了两篇关于私人类型的博客文章,值得一读:

答案 1 :(得分:2)

通过某些基类型的值列表定义类型是有意义的,但OCaml没有类似的类型。它有一组原始类型,如intchar。您可以定义自己的新基元类型,其值为YesNo等文字。 (当您定义这样的文字时,它看起来像一个大写的标识符。)您可以将它们与参数化类型(如列表,数组等)组合在一起。

如果您确实希望将int值限制在某个范围内,则可以将其定义为模块接口隐藏的抽象类型。在模块中,您需要在限制的整数范围内定义要支持的所有操作。 (请注意,在通常的算术运算下,这些类型不会关闭。)

您还可以定义:

type rgbint = RBG0 | RGB1 | RGB2 | RGB3 | RGB4 | RGB5

在实践中,这可能是你最终要做的事情,但是当你把基础价值看作数字时,这种类型会感觉很麻烦。