在类型级别将自定义类型定义为整数的子集

时间:2017-01-02 05:23:52

标签: types ocaml

我对在较大图像类型的层次结构中定义基元类型(颜色通道)感兴趣。由于所讨论的类型表示一点,将其可能值限制为仅0到255范围内的整数似乎是明智的。但是,我不知道在类型级别实现这些约束是否通常在OCaml的类型系统中完成。

type channel = int (* restrict to range 0 -> 255 *)

如果在OCaml类型系统中这样做是合法的,那么应该如何在类型级别定义对整数集的约束? 更具体地说,如何将类型定义为整数的子集(范围)?

3 个答案:

答案 0 :(得分:4)

另一种解决方案是定义具有私有类型的模块,如下所示:

module type INTERVAL = sig
  type t = private int
  val of_int : int -> t (* fails if value outside bounds *)
end

let interval a b = (module struct
  type t = int

  let of_int v =
    if v < a || v > b
    then failwith (Printf.sprintf "Not in interval [%d, %d]" a b)
    else v
end : INTERVAL)

使用示例:

let () =
  let module Small_int = (val (interval 0 255) : INTERVAL) in
  let x = Small_int.of_int (read_int ()) in
  print_int ((x :> int) * 2)

以这种方式构建的模块允许您使用一组受限制的整数值。但是它们有几个缺点:

  • 您必须先将值转换回整数才能在其上使用运算符(使用:&gt;运算符,如示例所示);
  • 类型本身不会提供有关允许的实际边界的任何信息;您必须查看实现或阅读文档以了解类型的含义;
  • 使用的内存空间是一个整数,在这种情况下不是一个字节;
  • 如果您使用相同的边界实例化两个模块,则它们的类型t将不兼容。

关于第一个缺点,没有任何限制您将操作添加到模块类型:

module type INTERVAL = sig
  type t = private int
  val of_int : int -> t
  val ( + ) : t -> t -> t
  val print_int : t -> unit
end

let interval a b = (module struct
  type t = int

  let of_int v =
    if v < a || v > b
    then failwith "Not in interval"
    else v

  let ( + ) x y = of_int (x + y)

  let print_int x = print_int x
end : INTERVAL)


let () =
  let module Small_int = (val (interval 0 255) : INTERVAL) in
  let x = Small_int.of_int (read_int ()) in
  let y = Small_int.of_int (read_int ()) in
  Small_int.(print_int (x + y));
  print_newline ()

使用您在项目文档中声明的一些约定可以克服第二个缺点。

当您想要确保给予函数的输入处于某个“合法”范围时,这有时很有用。如果您是OCaml的新手,我不确定您是否想要使用它,但是知道它可以完成它仍然很有用。

答案 1 :(得分:1)

如果您定义了这样的子类型,Ocaml将如何派生适用于此子类型的操作? (+, - ,*, - )。以及初始类型与其子类型之间的操作?

除Ocaml中的对象外,无法通过缩小其他类型的定义来定义约束类型。

在您的情况下,channel应映射到char类型 - 但它仍然是定义实现算术所需的所有操作。

答案 2 :(得分:1)

也许你可以使用Functor:

module type Elt_type =
  sig
    type t
    val min       : t
    val max       : t
    val (<)       : t -> t -> bool
    val (>)       : t -> t -> bool
    val (+)       : t -> t -> t
    val print     : t -> unit
  end

module Make (Elt : Elt_type)  =
  struct
    type t'      = Set of Elt.t
    let of_t x   = 
      if Elt.(<) x Elt.min || Elt.(>) x Elt.max
          then failwith "Not in interval"
      else Set x
    let to_t x'  = let Set x=x' in x
    let (+) x' y'= of_t (Elt.(+) (to_t x') (to_t y'))
    let print x' = Elt.print (to_t x')
  end

module Int =Make (
  struct
    type t       = int
    let  min     = 1
    let  max     = 10
    let  (<)     = Pervasives.(<)
    let  (>)     = Pervasives.(>)
    let  (+)     = Pervasives.(+)
    let print    = Pervasives.print_int
  end)

测试:

# open Int
# let x = of_t 2;;
val x : Int.t' = Set 2
# let y = of_t 3;;
val y : Int.t' = Set 3
# print (x + y);;
5- : unit = ()