OCaml中的参数化

时间:2014-11-16 18:04:34

标签: generics ocaml containers

我是OCaml的完全初学者,但我在介绍性示例中看到的代码类似于以下内容

let sum = List.fold ~f:(+.) ~init:0.0

在这段代码中让我烦恼的是明确使用List.fold。在我所知道的大多数语言中,容器的显式类型将通过使用接口,特征或类型类来抽象,以便可以重用此代码来对数组或任何其他顺序容器求和。

什么是OCaml方式使其更通用?

1 个答案:

答案 0 :(得分:7)

更通用的解决方案是使用仿函数:

module type Foldable = sig 
  type 'a t
  val fold : 'a t -> init:'b -> f:('b -> 'a -> 'b) -> 'b
end

module MakeSum(Container : Foldable) = struct
  let sum xs = Container.fold ~f:(+.) ~init:0.0
end

正如您可能已经注意到,这种额外的参数化带来了显着的语法开销。实际上,我们可以重用Core中的接口来减少它:

module MakeSum(Container : Container.T) = struct
  let sum xs = Container.fold ~f:(+.) ~init:0.0
end

module MakeSum(Container : Container.S1) = struct
  let sum xs = Container.fold ~f:(+.) ~init:0.0
end

另外,作为一个例子,您可能最感兴趣的不是对容器类型进行参数化,而是对求和运算和零值进行参数化。这个特定的例子是使用OCaml Core库中的第一类函子(另一个选项)实现的,每个容器都实现了这个函数:

  (** Returns the sum of [f i] for i in the container *)
  val sum
    : (module Commutative_group.S with type t = 'sum)
    -> t -> f:(elt -> 'sum) -> 'sum

更新

实际上,在您的特定情况下,即使没有仿函数,我们也可以概括,因为您不需要知道类型以实现您的代码。因此,我们可以将通用sum定义为:

let sum fold xs = fold ~f:(+.) ~init:0.0