【问题标题】:Preserving invariants while allowing destructuring在允许解构的同时保留不变量
【发布时间】:2012-05-22 20:50:46
【问题描述】:

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

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

【问题讨论】:

    标签: pattern-matching ocaml invariants


    【解决方案1】:

    正如manual 中所述,您需要显式强制:

    # let (a, b) = (r :> int*int);;
    val a : int = 1
    val b : int = 2
    

    【讨论】:

    • 感谢您对手册的指点。当名义类型和已擦除类型之间的强制自动发生以及不发生时,我需要一段时间才能理解。类型强制运算符的存在是否意味着解构始终是可能的——所以除了通过函数封闭的值之外,没有可能隐藏真正的信息(例如可变或秘密)?
    • 在 1) 透明类型、2) 抽象类型、3a) 私有变体或记录类型 3b) 私有类型缩写之间存在差异。对于 1) 强制是可能的两种方式并且是隐含的。对于 2) 根本不可能进行强制。对于 3a) 强制是一种可能的方式,而且似乎是隐含的。对于 3b) 强制也是一种可能的方式,而且似乎是明确的。需要注意的是,强制转换没有计算内容(它们在运行时被擦除)。
    • 谢谢。所以类型系统是两层的——一个是基本的,区分具体类型和抽象类型,影响消息分发,另一个是表面的,需要擦除,可以排除分发但不影响分发。
    【解决方案2】:

    一个简单的方法是添加一个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
    

    【讨论】:

    • 您的解决方案有效。与使用私有类型的区别在于to_tuple 具有计算内容,而强制 (_ :> _) 没有。要理解这一点,请考虑Range.t list 类型的值并尝试将其转换为(int*int) list 类型的列表。使用您的解决方案,您需要使用List.map to_tuple _(复制列表);使用私有类型解决方案,您只写 (_ :&gt; (int*int) list) 什么都不做(强制被删除)。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      我刚刚使用 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}
      

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

      【讨论】:

      • 我认为你没有回答这个问题。
      • 我试图回答他说“我可以将类型更改为 type t = R of (int * int) 但我需要这些尽可能轻量级内存的部分” 。 有任何想法吗?”我的主张是 R of int * int 在内存方面不是重量级类型。
      【解决方案5】:

      其实OCaml Runtime 编码很简单。 这是一个您可能会发现有用的工具 https://github.com/bobzhang/caml-inspect

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-02
        • 2013-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-20
        • 2021-02-07
        相关资源
        最近更新 更多