OCaml类型构造函数帮助。我需要进一步理解这一点

时间:2017-10-25 05:32:49

标签: ocaml

有序列表是一个列表,其中列表中稍后出现的元素永远不会小于列表中较早出现的元素,其中“小于”的概念由特定选择的关系给出。例如,列表

   [1; 5; 7; 12; 13]

是相对于通常<的有序列表。关于数字的关系,列表

   [17; 14; 9; 6; 2]

是相对于>的有序列表。数字上的关系但不是相对于<订单和清单

  [17; 14; 9; 13; 2]

我如何定义类似于列表类型构造函数的类型构造函数olist,除了这种类型的值带有足够的信息来确定它们是否是相对于预期排序关系的有序列表?

例如,我需要一个

的功能
initOList : ('a -> 'a -> bool) -> 'a olist

获取给定类型的排序关系并返回该类型的空olist。我在创建这种类型的教师时使用它来创建initOList函数?

2 个答案:

答案 0 :(得分:1)

静态类型表示可以在编译时检查的表达式的某些属性,即,不依赖于仅在运行时可用的任何信息。因此,您无法在类型系统中真正表达依赖于运行时值的事物,例如列表长度,排序,内容等。

在您的特定情况下,您希望拥有一个采用排序并返回新类型的类型构造函数。但是,类型构造函数是类型域中的函数,即它们将类型映射到类型(即,将类型作为其参数和返回类型)。但是排序是'a -> 'a -> int类型的函数,因此您无法在OCaml中真正表达它。

允许值出现在类型构造函数的域中的类型系统(即,使用运行时值参数化类型)称为从属类型系统。 OCaml并不真正提供依赖类型。这不是因为不可能这样做,这是一个选择问题,因为使用依赖类型系统要困难得多,而且依赖类型系统中的类型推断它是不可判定的(即,不可能写一个算法将适用于所有程序),因此通常需要用户的重要帮助来证明程序是良好类型的。因此,实现依赖类型的系统更接近自动化定理证明,例如CoqIsabelle等。但是,有些更接近于传统编程语言,例如{{ 3}}

现在,我们很清楚,在OCaml中,类型不能使用运行时值进行参数化。然而,打字系统仍然提供足够的能力来表达排序列表的概念。我们无法真正使用类型系统检查我们的排序算法是否正确,但我们可以告诉它,嘿,这个函数确保列表已排序,只相信我们,并且把它视为理所当然。我们可以使用模块级抽象来表示这一点,例如,

module SortedList : sig 
  type 'a t
  val create : ('a -> 'a -> int) -> 'a list -> 'a t
end = struct
  type 'a t = 'a list
  let create = List.sort
end

我们的想法是,我们的类型'a SortedList.t只能使用SortedList.create函数构建,并且没有其他方法可以解决它。因此,类型为'a SortedList.t的表达式本身就有证明,它已被排序。我们可以使用它在类型系统中使函数的前提条件显式化,例如,假设我们有一个dedup函数可以从列表中删除重复项,并且如果输入列表已排序,它可以正常工作。我们无法真正测试这个前提条件,因此我们可能依赖于类型系统:

val dedup : 'a SortedList.t -> 'a SortedList.t

因此,重复数据删除函数类型表明(a)它仅适用于排序列表,(b)它保留了排序。

我们的方法有两个问题。首先,我们的'a SortedList.t过于抽象,因为它没有提供任何访问运营商。我们可以提供一个to_list运算符,它将删除列表的排序证明,但允许对其进行所有列表操作,例如,

module SortedList : sig 
  type 'a t
  val create : ('a -> 'a -> int) -> 'a list -> 'a t
  val to_list : 'a t -> 'a list
end = struct
  type 'a t = 'a list
  let create = List.sort
  let to_list xs = xs
end

to_list操作是正确的,因为所有排序列表的集合是所有列表的子集。这意味着,'a SortedList.t实际上是'a list的子类型。很好,OCaml提供了一种通过私有抽象类型表达这种子类型关系的显式机制。

module SortedList : sig 
  type 'a t = private 'a list
  val create : ('a -> 'a -> int) -> 'a list -> 'a t
end = struct
  type 'a t = 'a list
  let create = List.sort
end

类型的这种定义表明'a SortedList.t'a list的子类型,因此可以将一个转换为另一个。请记住,由于向上转换在OCaml中是明确的,因此它不是自动的,因此您需要使用向上转换运算符,例如xs : 'a SortedList.t :> 'a list

我们实现的第二个问题是我们的'a SortedList.t并没有真正区分不同排序的列表,即升序或降序。这对于dedup函数来说不是问题,但是某些函数可能要求它们的输入按特定顺序排序(例如,将找到中值或任何其他统计模式的函数)。所以我们需要对订购进行编码。我们将以这种方式编码排序,我们将每个排序函数视为不同的类型。 (另一种解决方案是编码具体变体,例如AscendingDescending,但我会留下这是一个练习)。我们的方法的主要缺点是我们的有序列表不再是参数类型,因为订购函数是针对特定地面类型定义的。实际上,这意味着我们的OrderedList.t现在是一个更高阶的多态类型,所以我们需要使用仿函数来实现它:

module type Ordering = sig
  type element
  type witness

  val compare : element -> element -> int
end

module SortedList  : sig

  type ('a,'o) t = private 'a list

  module Make(Order : Ordering) : sig
    type nonrec t = (Order.element, Order.witness) t
    val create : Order.element list -> t
  end

end = struct

  type ('a,'o) t = 'a list

  module Make(Order : Ordering) = struct
    type nonrec t = (Order.element, Order.witness) t
    let create = List.sort Order.compare
  end
end

现在,让我们对我们的实施进行一些调整。让我们提供给不同的订单:

module AscendingInt = struct
  type element = int
  type witness = Ascending_int
  let compare : int -> int -> int = compare
end

module DescendingInt = struct
  type element = int
  type witness = Descending_int
  let compare : int -> int -> int = fun x y -> compare y x
end


module AscendingSortedList = SortedList.Make(AscendingInt)
module DescendingSortedList = SortedList.Make(DescendingInt)

现在,让我们测试两个排序列表实际上有不同的类型:

# let asorted = AscendingSortedList.create [3;2;1];;
val asorted : AscendingSortedList.t = [1; 2; 3]
# let bsorted = DescendingSortedList.create [3;2;1];;
val bsorted : DescendingSortedList.t = [3; 2; 1]
# compare asorted bsorted;;
Characters 16-23:
  compare asorted bsorted;;
                  ^^^^^^^
Error: This expression has type
         DescendingSortedList.t =
           (DescendingInt.element, DescendingInt.witness) SortedList.t
       but an expression was expected of type
         AscendingSortedList.t =
           (AscendingInt.element, AscendingInt.witness) SortedList.t
       Type DescendingInt.witness is not compatible with type
         AscendingInt.witness 

现在让我们检查一下它们实际上是'a list的子类型:

# compare (asorted :> int list) (bsorted :> int list);;
- : int = -1

答案 1 :(得分:0)

根据您提供的定义,olist只是list,附加了比较功能。这可以使用记录轻松实现(您可以阅读更多关于它们的here)。

从那里,您可以编写创建和操作新类型值的函数。

我们需要更深入

如果您已设法实施上述解决方案,我建议您将所有定义放在模块中。您可以阅读有关这些here的更多信息。简而言之,模块是一种将类型定义和函数放在一起的方法。