我有一个范围类型定义为:
type 'a range = Full | Range of ('a * 'a)
但是,我想将'a'限制为整数或浮点数或字符,而没有其他有效类型的'a。
Range(0,10) (* valid *)
Range(0.0, 10.0) (* valid *)
Range('a', 'z') (* valid *)
Range("string1", "string2") (* other types like this shouldn't type check *)
我想我可以将我的类型定义更改为:
type sequential = S_int of int | S_float of float | S_char of char ;;
type range = Full | Range of (sequential * sequential);;
然而,这将允许类似:
Range(S_int(0), S_float(10.0));; (* problem: mixes int and float *)
...但我希望Range的两个组件都是相同的类型。
我认为另一种方法是创建一个int_range类型,一个float_range类型和一个char_range类型,但我想知道是否还有另一种方式?
答案 0 :(得分:12)
另一种方法是声明类型为private并公开仅使用您想要的类型构建它的函数,例如: :
module Z : sig
type 'a range = private Full | Range of ('a * 'a)
val int_range : int -> int -> int range
val float_range : float -> float -> float range
val string_range : string -> string -> string range
val full : 'a range
end = struct
type 'a range = Full | Range of ('a * 'a)
let create x y = Range (x,y)
let int_range = create
let float_range = create
let string_range = create
let full = Full
end
# open Z;;
# int_range 2 3;;
- : int Z.range = Range (2, 3)
# Range ('a','c');;
Error: Cannot create values of the private type char Z.range
答案 1 :(得分:10)
从Haskell的作用中提示(声明一个类型类(Sequential a) => Range a
)你可以使用一个仿函数:
module Range (S : sig type t end) = struct
type range = Full | Range of (S.t * S.t)
end
并使用它来提供所需的模块:
module IntRange = Range (struct type t = int end)
module FloatRange = Range (struct type t = float end)
module CharRange = Range (struct type t = char end)
缺点是你失去range
的参数;好处是range
上的参数函数现在位于模块Range
内部,因为它们可能应该存在。
通常,Range
会对Sequential
s提出一些要求,以弥补参数的损失。可以在仿函数参数的签名中清晰地指定这些要求:
module type SEQUENTIAL = sig
type t
val to_string : t -> string
val compare : t -> t -> int
(* ... *)
end
module Range (S : SEQUENTIAL) = struct
type t = Full | Range of (S.t * S.t)
let to_string = function
| Full -> "full"
| Range (lo, hi) -> "(" ^ S.to_string lo ^ "," ^ S.to_string hi ^ ")"
let make lo hi =
if S.compare lo hi > 0 then Range (hi, lo) else Range (lo, hi)
end
要在特定类型中实例化Range
,您现在需要提供一个正确参数化的结构:
module IntRange = Range (struct
type t = int
let to_string = string_of_int
let compare = Pervasives.compare
end)
然后你可以像这样使用它:
# IntRange.(to_string (make 4 2)) ;;
- : string = "(2,4)"
(使用新语法进行分隔重载)。如果您需要在签名后隐藏Range
s的实现,则可能需要重新导出SEQUENTIAL
的类型,就像标准库中的数据结构一样:
module Range (S : SEQUENTIAL) : sig
type elt = S.t
type t = private Full | Range of (elt * elt)
val to_string : t -> string
val make : elt -> elt -> t
end = struct
type elt = S.t
type t = Full | Range of (elt * elt)
let to_string = function
| Full -> "full"
| Range (lo, hi) -> "(" ^ S.to_string lo ^ "," ^ S.to_string hi ^ ")"
let make lo hi =
if S.compare lo hi > 0 then Range (hi, lo) else Range (lo, hi)
end
这为封装和半透明类型提供了模式匹配但不构造的类型。在签名中声明private
类型的替代方法是使用视图类型或解构函数。
答案 2 :(得分:0)
OMG模块太复杂了!
type 'a range' = [`Full | `Range of 'a * 'a]
type range = [
| `Int_range of int range'
| `Float_range of float range'
]
哦,我们需要添加另一个:
type xrange = [
| range
| `String_range of string range'
]