7

我想定义一个类型,以便所有构造都通过可以保留不变量的模块成员,但允许解构以进行模式匹配。

我只是在学习 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)但我需要这些尽可能轻量级的内存。有任何想法吗?

4

5 回答 5

9

手册中所述,您需要显式强制:

# let (a, b) = (r :> int*int);;
val a : int = 1
val b : int = 2
于 2012-05-22T21:01:02.940 回答
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
于 2012-05-23T05:37:56.303 回答
3

就内存而言,您的解决方案type t = R of (int * int)将是轻量级的,并且在语法上比强制解决方案更轻量级。OCaml 优化了单构造函数数据类型的情况,因此您无需为此付费。(我没有对此声明的官方参考,但 Adam Chlipala(OCaml 专家)在这里提到:http: //adam.chlipala.net/cpdt/html/Subset.html)。

于 2012-05-31T20:57:23.853 回答
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}

如果您相信这些值(我相信),那么使用您自己的单构造函数类型而不是元组不会有大小损失。

于 2012-05-22T21:26:37.957 回答
1

实际上 OCaml 运行时编码非常简单。这是一个您可能会发现有用的工具 https://github.com/bobzhang/caml-inspect

于 2012-06-03T21:15:55.093 回答