如何将 OCaml 整数类型限制为整数范围?
How can I constrain an OCaml integer type to a range of integers?
阅读 Real World 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() 定义的操作?
感谢和祝福
伊万
通过一些基本类型的值列表来定义类型是有意义的,但 OCaml 没有这样的类型。它有一组原始类型,如 int
和 char
。您可以定义自己的新原始类型,其值是 Yes
和 No
等文字。 (当你定义这样一个文字时,它看起来像一个大写的标识符。)你可以将它们与参数化类型(如列表、数组等)结合起来。
如果你真的想要一个int值限制在某个范围内,你可以将它定义为一个被模块接口隐藏的抽象类型。在该模块中,您需要定义您希望在限制的整数范围内支持的所有操作。 (请注意,此类类型在通常的算术运算下不会关闭。)
您还可以定义:
type rgbint = RBG0 | RGB1 | RGB2 | RGB3 | RGB4 | RGB5
在实践中,这可能就是您最终会做的事情,尽管当您将基础值视为数字时,这种类型感觉很麻烦。
这是私有类型的典型用例
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)
无法自 assemble 一个 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 写了两篇关于私有类型的博文,值得一读:
阅读 Real World 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() 定义的操作?
感谢和祝福
伊万
通过一些基本类型的值列表来定义类型是有意义的,但 OCaml 没有这样的类型。它有一组原始类型,如 int
和 char
。您可以定义自己的新原始类型,其值是 Yes
和 No
等文字。 (当你定义这样一个文字时,它看起来像一个大写的标识符。)你可以将它们与参数化类型(如列表、数组等)结合起来。
如果你真的想要一个int值限制在某个范围内,你可以将它定义为一个被模块接口隐藏的抽象类型。在该模块中,您需要定义您希望在限制的整数范围内支持的所有操作。 (请注意,此类类型在通常的算术运算下不会关闭。)
您还可以定义:
type rgbint = RBG0 | RGB1 | RGB2 | RGB3 | RGB4 | RGB5
在实践中,这可能就是您最终会做的事情,尽管当您将基础值视为数字时,这种类型感觉很麻烦。
这是私有类型的典型用例
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)
无法自 assemble 一个 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 写了两篇关于私有类型的博文,值得一读: