#module Fifo = structtype 'a queue = { front: 'a list; rear: 'a list } let make front rear = match front with | [] -> { front = List.rev rear; rear = [] } | _ -> { front; rear } let empty = { front = []; rear = [] } let is_empty = function { front = []; _ } -> true | _ -> falselet add x q = make q.front (x :: q.rear) exception Empty let top = function | { front = []; _ } -> raise Empty | { front = x :: _; _ } -> x let pop = function | { front = []; _ } -> raise Empty | { front = _ :: f; rear = r } -> make f r end;;
module Fifo : sigtype 'a queue = { front : 'a list; rear : 'a list; } val make : 'a list -> 'a list -> 'a queue val empty : 'a queue val is_empty : 'a queue -> bool val add : 'a -> 'a queue -> 'a queue exception Empty val top : 'a queue -> 'a val pop : 'a queue -> 'a queue end
#module FifoOpt = structinclude Fifo let top_opt q = if is_empty q then None else Some(top q) let pop_opt q = if is_empty q then None else Some(pop q) end;;
module FifoOpt : sigtype 'a queue = 'a Fifo.queue = { front : 'a list; rear : 'a list; } val make : 'a list -> 'a list -> 'a queue val empty : 'a queue val is_empty : 'a queue -> bool val add : 'a -> 'a queue -> 'a queue exception Empty val top : 'a queue -> 'a val pop : 'a queue -> 'a queue val top_opt : 'a queue -> 'a option val pop_opt : 'a queue -> 'a queue option end
签名是结构体的接口。签名指定结构体的哪些组件可从外部访问,以及使用哪种类型。它可以用来隐藏结构体的某些组件(例如局部函数定义)或以受限类型导出某些组件。例如,以下签名指定队列操作empty、add、top和pop,但不包括辅助函数make。类似地,它使queue类型成为抽象类型(不提供其作为具体类型的实际表示形式)。这确保了Fifo模块的用户无法违反操作依赖的数据结构不变式,例如“如果 front 列表为空,则 rear 列表也必须为空”。
#moduletype FIFO = sigtype 'a queue (* 现在是抽象类型 *)val empty : 'a queue val add : 'a -> 'a queue -> 'a queue val top : 'a queue -> 'a val pop : 'a queue -> 'a queue exception Empty end;;
moduletype FIFO = sigtype 'a queue val empty : 'a queue val add : 'a -> 'a queue -> 'a queue val top : 'a queue -> 'a val pop : 'a queue -> 'a queue exception Empty end
#moduletype FIFO_WITH_OPT = siginclude FIFO val top_opt: 'a queue -> 'a option val pop_opt: 'a queue -> 'a queue option end;;
moduletype FIFO_WITH_OPT = sigtype 'a queue val empty : 'a queue val add : 'a -> 'a queue -> 'a queue val top : 'a queue -> 'a val pop : 'a queue -> 'a queue exception Empty val top_opt : 'a queue -> 'a option val pop_opt : 'a queue -> 'a queue option end
函子(Functor)是将模块映射到模块的“函数”。函子允许你创建参数化的模块,然后提供其他模块作为参数来获得特定的实现。例如,一个使用排序列表实现集合的 Set 模块可以被参数化,使其能够处理任何提供元素类型和比较函数 compare 的模块(例如 OrderedString)。
#type comparison = Less | Equal | Greater;;
type comparison = Less | Equal | Greater
#moduletype ORDERED_TYPE = sigtype t val compare: t -> t -> comparison end;;
moduletype ORDERED_TYPE = sigtype t val compare : t -> t -> comparison end
#module Set = functor (Elt: ORDERED_TYPE) -> structtype element = Elt.t type set = element list let empty = [] letrec add x s = match s with [] -> [x] | hd::tl -> match Elt.compare x hd with Equal -> s (* x is already in s *) | Less -> x :: s (* x is smaller than all elements of s *) | Greater -> hd :: add x tl letrec member x s = match s with [] -> false | hd::tl -> match Elt.compare x hd with Equal -> true(* x belongs to s *) | Less -> false(* x is smaller than all elements of s *) | Greater -> member x tl end;;
module Set : functor (Elt : ORDERED_TYPE) -> sigtype element = Elt.t type set = element list val empty : 'a list val add : Elt.t -> Elt.t list -> Elt.t list val member : Elt.t -> Elt.t list -> bool end
通过将 Set 函子应用于实现有序类型的结构,我们就可以获得该类型的集合操作。
#module OrderedString = structtype t = string let compare x y = if x = y then Equal elseif x < y then Less else Greater end;;
module OrderedString : sigtype t = string val compare : 'a -> 'a -> comparison end
#module StringSet = Set(OrderedString);;
module StringSet : sigtype element = OrderedString.t type set = element list val empty : 'a list val add : OrderedString.t -> OrderedString.t list -> OrderedString.t list val member : OrderedString.t -> OrderedString.t list -> bool end
就像在 Fifo 示例中一样,隐藏类型 set 的实际实现是一种良好的风格,这样使用该结构的用户就不会依赖于集合是列表,并且我们以后可以切换到另一种更有效的集合表示方式,而不会破坏他们的代码。这可以通过使用合适的函子签名来限制 Set 来实现。
#moduletype SETFUNCTOR = functor (Elt: ORDERED_TYPE) -> sigtype element = Elt.t (* concrete *)type set (* abstract *)val empty : set val add : element -> set -> set val member : element -> set -> bool end;;
moduletype SETFUNCTOR = functor (Elt : ORDERED_TYPE) -> sigtype element = Elt.t type set val empty : set val add : element -> set -> set val member : element -> set -> bool end
module AbstractStringSet : sigtype element = OrderedString.t type set = AbstractSet(OrderedString).set val empty : set val add : element -> set -> set val member : element -> set -> bool end
module WrongStringSet : sigtype element = WrongSet(OrderedString).element type set = WrongSet(OrderedString).set val empty : set val add : element -> set -> set val member : element -> set -> bool end
Error: This expression has type string but an expression was expected of type WrongStringSet.element = WrongSet(OrderedString).element
这里的问题是 SET 以抽象的方式指定了类型 element,因此函子结果中的 element 与其参数中的 t 之间的类型相等性被遗忘了。因此,WrongStringSet.element 与 string 不是同一类型,并且 WrongStringSet 的操作不能应用于字符串。如上所示,在签名 SET 中,类型 element 必须声明为与 Elt.t 相等;不幸的是,这在上面是不可能的,因为 SET 在一个 Elt 不存在的上下文中定义。为了克服这个困难,OCaml 提供了一个 with type 结构,它允许用额外的类型相等性来丰富签名。
module AbstractSet2 : functor (Elt : ORDERED_TYPE) -> sigtype element = Elt.t type set val empty : set val add : element -> set -> set val member : element -> set -> bool end
与简单结构的情况一样,OCaml 提供了另一种语法来定义函子并限制其结果。
module AbstractSet2(Elt: ORDERED_TYPE) : (SET with type element = Elt.t) =
struct ... end;;
module NoCaseStringSet : sigtype element = NoCaseString.t type set = AbstractSet(NoCaseString).set val empty : set val add : element -> set -> set val member : element -> set -> bool end
module Aux: sig (* contents of Aux.mli *) end
= struct (* contents of Aux.ml *) end;;
module Main: sig (* contents of Main.mli *) end
= struct (* contents of Main.ml *) end;;