Types 约束多态类型

Types 约束多态类型,types,functional-programming,ocaml,Types,Functional Programming,Ocaml,我将范围类型定义为: 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 '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 *)
…但我希望范围的两个组件都是同一类型


我想另一种方法是创建int\u range类型、float\u range类型和char\u range类型,但我想知道是否还有其他方法?

另一种方法是声明类型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

从Haskell将要做的事情(声明一个类型类
(顺序a)=>范围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
s会对
Sequential
s提出许多要求,以补偿参数的损失。这些要求可以在functor参数的签名中明确指定:

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
要在特定类型上实例化
范围
,您现在需要提供一个适当参数化它的结构:

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
s的类型,就像标准库中的数据结构一样:

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

这为您提供了封装和半透明类型,这些类型可以进行模式匹配,但不能构造。在签名中声明
私有
类型的替代方法是使用视图类型或解构函数。

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'
]

很好,但是把它带到下一个层次:假设我想将范围转换为字符串:我如何在范围模块中定义一个to_字符串函数,该函数可以(s.t->string)?您需要
顺序
(作为模块签名)为
范围
提供一个
to_字符串
。实际上,
Range
S对
S.t
S的每个特定要求都必须在functor参数中明确规定。要使用它:module IntRange=Range(struct type t=int let to_string v=string of_int v end);;设r=IntRange.Range(1,10);;内部传送到字符串r@是的,我对这个例子做了一些扩展,展示了如何实例化模块,如何使用生成的模块以及可能的扩展途径。@Matías,谢谢,文档记录得很好。一个问题:这是3.12中用于分隔重载的新语法吗?或者它是camlp4语法扩展?当我用谷歌搜索这个词时,它似乎是后者,而不是内置的。这是提议的3.13特性吗?但这允许:
Int_范围('a','z');:[>
Int_-range of char*char]=`Int_-range('a','z')(例如)。