简体   繁体   English

在OCaml中实现Okasaki的自举堆,为什么不编译?

[英]Implementing Okasaki's bootstrapped heaps in OCaml, why doesn't it compile?

(A minimal non-compiling example can be found at https://gist.github.com/4044467 , see more background below.) (一个最小的非编译示例可以在https://gist.github.com/4044467上找到,请参阅下面的更多背景。)

I am trying to implement Bootstrapped Heaps introduced in Chapter 10 of Okasaki's Purely Functional Data Structure . 我正在尝试实现Okasaki的Pure Functional Data Structure第10章中介绍的Bootstrapped堆 The following is a simplified version of my non-compiling code. 以下是我的非编译代码的简化版本。

We're to implement a heap with following signature: 我们将使用以下签名实现堆:

module type ORDERED =
sig
  type t
  val compare : t -> t -> int
end

module type HEAP =
sig
  module Elem : ORDERED

  type heap

  val empty : heap
  val insert : Elem.t -> heap -> heap
  val find_min : heap -> Elem.t
  val delete_min : heap -> heap
end

We say a data structure is bootstrapped when its implementation depends on another implementation of the same kind of data structure. 我们说一个数据结构的实现依赖于相同类型数据结构的另一个实现而被引导 So we have a heap like this (the actual implementation is not important): 因此,我们有一个这样的堆(实际的实现并不重要):

module SomeHeap (Element : ORDERED) : (HEAP with module Elem = Element) =
struct
  module Elem = Element

  type heap

  let empty = failwith "skipped"
  let insert = failwith "skipped"
  let find_min = failwith "skipped"
  let delete_min = failwith "skipped"
end 

Then, the bootstrapped heap we're going to implement, which can depend on any heap implementation, is supposed to have the following signature: 然后,我们将要实现的自举堆(可能依赖于任何堆实现)应该具有以下签名:

module BootstrappedHeap
  (MakeH : functor (Element : ORDERED) -> HEAP with module Elem = Element)
  (Element : ORDERED) : (HEAP with module Elem = Element)

So we can use it like this: 因此我们可以像这样使用它:

module StringHeap = BootstrappedHeap(SomeHeap)(String)

The implementation of BootstrappedHeap, according to Okasaki, is like this: 冈崎说,BootstrappedHeap的实现是这样的:

module BootstrappedHeap
  (MakeH : functor (Element : ORDERED) -> HEAP with module Elem = Element)
  (Element : ORDERED) : (HEAP with module Elem = Element) =
struct
  module Elem = Element

  module rec BootstrappedElem :
  sig
    type t =
      | E
      | H of Elem.t * PrimH.heap
    val compare : t -> t -> int
  end =
  struct
    type t =
      | E
      | H of Elem.t * PrimH.heap
    let compare t1 t2 = match t1, t2 with
      | H (x, _), H (y, _) -> Elem.compare x y
      | _ -> failwith "unreachable"
  end
  and PrimH : (HEAP with module Elem = BootstrappedElem) =
    MakeH(BootstrappedElem)

  type heap

  let empty = failwith "not implemented"
  let insert = failwith "not implemented"
  let find_min = failwith "not implemented"
  let delete_min = failwith "not implemented"
end

But this is not compiling! 但这还没有编译! The error message is: 错误消息是:

File "ordered.ml", line 52, characters 15-55:
Error: In this `with' constraint, the new definition of Elem
       does not match its original definition in the constrained signature:
       Modules do not match:
         sig type t = BootstrappedElem.t end
       is not included in
         ORDERED
       The field `compare' is required but not provided

The line 52 is the line 52行是

and PrimH : (HEAP with module Elem = BootstrappedElem) =

I think BootstrappedElem did implement ORDERED as it has both t and compare , but I failed to see why the compiler fails to find the compare function. 我认为BootstrappedElem确实实现了ORDERED因为它同时具有tcompare ,但是我看不到为什么编译器无法找到compare函数。

Change the signature of BootstrappedElem to 将BootstrappedElem的签名更改为

module rec BootstrappedElem : ORDERED

will make it compiling but this will hide the type constructor E and T in BootstrappedElem to make it impossible to implement the later parts. 将对其进行编译,但这将在BootstrappedElem隐藏类型构造函数ET ,以致于无法实现后面的部分。

The whole non-compiling code can be downloaded at https://raw.github.com/gist/4044281/0ce0336c40b277e59cece43dbadb9b94ce6efdaf/ordered.ml 整个非编译代码可以从https://raw.github.com/gist/4044281/0ce0336c40b277e59cece43dbadb9b94ce6efdaf/ordered.ml下载

I believe this might be a bug in the type-checker. 我相信这可能是类型检查器中的错误。 I have reduced your code to the following example: 我已将您的代码简化为以下示例:

module type ORDERED =
sig
  type t
  val compare : t -> t -> int
end


module type CARRY = sig
  module M : ORDERED
end

(* works *)
module HigherOrderFunctor
  (Make : functor (X : ORDERED) -> (CARRY with module M = X))
= struct
  module rec Base
    : (ORDERED with type t = string)
    = String
  and Other
    : (CARRY with module M = Base)
    = Make(Base)
end

(* does not work *)
module HigherOrderFunctor
  (Make : functor (X : ORDERED) -> (CARRY with module M = X))
= struct
  module rec Base
    : sig
      (* 'compare' seems dropped from this signature *)
      type t = string
      val compare : t -> t -> int
    end
    = String
  and Other
    : (CARRY with module M = (Base : sig type t = string val compare : t -> t -> int end))
    = Make(Base)
end

I don't understand why the first code works and the second (which seems equivalent) doesn't. 我不明白为什么第一个代码有效而第二个代码(看来等效)为什么不起作用。 I suggest you wait a bit to see if an expert comes with an explanation (Andreas?), then consider sending a bug report . 我建议您稍等一下,看看专家是否提供了解释(Andreas?),然后考虑发送错误报告

In this case, a solution is to first bind the signature that seems mishandled: 在这种情况下,一种解决方案是首先绑定似乎处理不当的签名:

(* works again *)
module HigherOrderFunctor
  (Make : functor (X : ORDERED) -> (CARRY with module M = X))
= struct
  (* bind the problematic signature first *)
  module type S = sig
    type t = string
    val compare : t -> t -> int
  end

  module rec Base : S = String
  and Other : (CARRY with module M = Base) = Make(Base)
end

However, that is not possible in your setting, because the signature of BootstrappedElem is mutually recursive with BootstrappedHeap . 但是,这是不可能在你的设置,因为签名BootstrappedElem是相互递归BootstrappedHeap

A workaround is to avoid the apparently-delicate with module ... construct and replace it with a simple type equality with type Elem.t = ... : 一种解决方法是避免使用明显的with module ...构造并将其替换with type Elem.t = ...的简单类型相等:

module BootstrappedHeap
  (MakeH : functor (Element : ORDERED) -> HEAP with module Elem = Element)
  (Element : ORDERED) : (HEAP with module Elem = Element) =
struct
  module Elem = Element

  module rec BootstrappedElem :
  sig
    type t =
      | E
      | H of Elem.t * PrimH.heap
    val compare : t -> t -> int
  end =
  struct
    type t =
      | E
      | H of Elem.t * PrimH.heap
    let compare t1 t2 = match t1, t2 with
      | H (x, _), H (y, _) -> Elem.compare x y
      | _ -> failwith "unreachable"
  end
  and PrimH : (HEAP with type Elem.t = BootstrappedElem.t) =
    MakeH(BootstrappedElem)

  type heap

  let empty = failwith "not implemented"
  let insert = failwith "not implemented"
  let find_min = failwith "not implemented"
  let delete_min = failwith "not implemented"
end

You could also avoid the mutual recursion and define both BootstrappedElem and BootstrappedHeap in one recursive knot, by defining BootstrappedElem inside the recursive BootstrappedHeap . 您也可避免相互递归和定义都BootstrappedElemBootstrappedHeap在一个递归结,通过定义BootstrappedElem递归 BootstrappedHeap

module BootstrappedHeap
  (MakeH : functor (Element : ORDERED) -> HEAP with module Elem = Element)
  (Element : ORDERED) : (HEAP with module Elem = Element) =
struct
  module rec BootstrappedHeap : sig
    module Elem : sig
      type t = E | H of Element.t * BootstrappedHeap.heap
      val compare : t -> t -> int
    end        
    include (HEAP with module Elem := Elem)
  end = struct
    module Elem = struct
      type t = E | H of Element.t * BootstrappedHeap.heap
      let compare t1 t2 = match t1, t2 with
        | H (x, _), H (y, _) -> Element.compare x y
        | _ -> failwith "unreachable"
    end
    include (MakeH(Elem) : HEAP with module Elem := Elem)
  end

  module Elem = Element

  type heap

  let empty = failwith "not implemented"
  let insert = failwith "not implemented"
  let find_min = failwith "not implemented"
  let delete_min = failwith "not implemented"
end

This style corresponds naturally to your decision of embedding Elem in the HEAP signature and using with module ... for refinement. 这种风格自然符合您决定将Elem嵌入HEAP签名并with module ...使用with module ...进行优化的决定。 Another solution would have been to define HEAP as a functor returning a signature, used as HEAP(Elem).S , and I suppose a different recursive style could have been chosed. 另一个解决方案是将HEAP定义为返回签名的函子,用作HEAP(Elem).S ,我想可以选择其他递归样式。 Not to say that this would have been better: I think the "abstract module" style is more convenient. 并不是说这样做会更好:我认为“抽象模块”样式更方便。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM