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')(例如)。