我想定义一个类型,以便所有构造都通过可以保留不变量的模块成员,但允许进行模式匹配的解构。
我只是在学习OCaml但是以下几乎适用于一个带有不变量的int对,左边应该严格小于右边
module Range : sig
type t = private { left:int; right:int }
exception InvalidRange of (int*int)
val make : int -> int -> t
end = struct
type t = { left:int; right:int }
exception InvalidRange of (int*int)
let make left right = if left < right
then { left; right }
else raise (InvalidRange (left, right))
end
因此
# let p = Range.make 1 2;;
val p : Range.t = {Range.left = 1; Range.right = 2}
# let q = Range.make 2 1;;
Exception: Range.InvalidRange (2, 1).
和解构在时尚之后起作用
# let {Range.left=x; Range.right=y} = p;;
val x : int = 1
val y : int = 2
构建失败时
# let badp = {Range.left = 2; Range.right = 1};;
let badp = {Range.left = 2; Range.right = 1};;
Error: Cannot create values of the private type Range.t
# open Range;;
# let badp = {left = 2; right=1};;
let badp = {left = 2; right=1};;
Error: Cannot create values of the private type Range.t
但我真正想做的是具有解构元组的语法方便性。 以下不起作用:
module Range : sig
type t = private int*int
exception InvalidRange of (int*int)
val make : int -> int -> t
end = struct
type t = int*int
exception InvalidRange of (int*int)
let make left right = if left < right
then (left, right)
else raise (InvalidRange (left, right))
end
然后我无法使用元组模式对其进行解构:
# let r = Range.make 1 2 ;;
val r : Range.t = (1, 2)
# let (a, b) = r;;
let (a, b) = r;;
Error: This expression has type Range.t
but an expression was expected of type 'a * 'b
我可以将类型更改为type t = R of (int * int)
,但我需要尽可能减轻记忆力。有什么想法吗?
答案 0 :(得分:9)
如manual中所述,您需要明确强制:
# let (a, b) = (r :> int*int);;
val a : int = 1
val b : int = 2
答案 1 :(得分:4)
一种简单的方法是添加to_tuple
函数并使浏览类型为抽象。
module Range : sig
type t
exception InvalidRange of (int*int)
val make : int -> int -> t
val to_tuple : t -> (int * int)
end = struct
type t = { left:int; right:int }
exception InvalidRange of (int*int)
let make left right = if left < right
then { left; right }
else raise (InvalidRange (left, right))
let to_tuple t = t.left, t.right
end
然后你可以做
let (a, b) = to_tuple range
答案 2 :(得分:3)
type t = R of (int * int)
的解决方案在内存方面将是轻量级的,并且在语法上比强制解决方案轻一点。 OCaml优化了单构造函数数据类型的情况,因此您无需支付费用。 (我没有关于这一主张的官方参考,但Adam Chlipala(OCaml专家)在此提及:http://adam.chlipala.net/cpdt/html/Subset.html)。
答案 3 :(得分:2)
我刚用Objsize运行此测试(报告OCaml值的大小)。
# type fancy = R of int * int;;
type fancy = R of int * int
# Objsize.objsize (R (3, 5));;
- : Objsize.info = {Objsize.data = 2; Objsize.headers = 1; Objsize.depth = 0}
# Objsize.objsize (3,5);;
- : Objsize.info = {Objsize.data = 2; Objsize.headers = 1; Objsize.depth = 0}
如果您相信这些值(我这样做),使用您自己的单构造函数类型而不是元组就没有大小代价。
答案 4 :(得分:1)
实际上OCaml Runtime编码非常简单。 这是一个你可能会觉得有用的工具 https://github.com/bobzhang/caml-inspect