使OCaml函数多态用于int列表和浮点列表

时间:2016-06-08 05:57:01

标签: polymorphism ocaml

有没有办法在OCaml中创建一个多态加法函数,对于整数和浮点数同样有效?例如,如果我有一个像:

这样的函数

partialsums [1; 2; 3; 4; 5]我应该[1; 3; 6; 10; 15]但是这个函数不能在[1.; 2.; 3.; 4.; 5.]上工作,因为在OCaml中,并且浮点数绝对不能混合。但是如果我希望我的函数同样适用于int列表和浮点列表呢?是否存在int和float是子类型的一般类型?如果是这样,它是什么?我在这一点上有点失落。谢谢你的帮助?

3 个答案:

答案 0 :(得分:13)

使用参数多态,没有。使用ad-hoc polymorphism,是。

对于某些类型 t ,请定义一个模块

module type Semigroup = sig
  type t
  val add : t -> t -> t
end

partialsums之类的一些实用函数在仿函数中依赖于它,

module Utils (S : Semigroup) = struct
  let partialsums xs =
      match xs with
      | [] -> []
      | (x::xs) ->
          List.rev (snd (List.fold_left
            (fun (acc, ys) x -> let y = S.add acc x in (y, y::ys)) (x, [x]) xs))
end

您可以将partialsums专门用于特定类型 t

module IntUtils = Utils(struct type t = int
                               let add = (+) end)
module FloatUtils = Utils(struct type t = float
                                 let add = (+.) end)

let int_test = IntUtils.partialsums [1; 2; 3; 4] ;;
let float_test = FloatUtils.partialsums [1.0; 2.0; 3.0; 4.0]

这很酷,但也有点乏味;你仍然需要为你的函数添加特定类型的东西,但至少你只需编写一次函数。这只是模块系统很棒。

模块化含义,是的,是的,是的!

使用White,Bour和Yallop的Modular Implicits (2014),你可以写,

implicit module Semigroup_int =
  type t = int
  let add = (+)
end

implicit module Semigroup_float =
  type t = float
  let add = (+.)
end

implicit module Semigroup_string =
  type t = string
  let add = (^)
end

let add {S : Semigroup} x y = S.add x y

允许定义泛型重载partialsums

let partialsums xs =
    match xs with
    | [] -> []
    | (x::xs) ->
        List.rev (snd (List.fold_left
          (fun (acc, ys) x -> let y = add acc x in (y, y::ys)) (x, [x]) xs))

所以现在它对于整数和浮点数同样有效!

let int_test = partialsums [1; 2; 3; 4] ;;
let float_test = partialsums [1.0; 2.0; 3.0; 4.0]
let string_test = partialsums ["a"; "b"; "c"; "d"]

显然有几次尝试统一ML模块系统和Haskell的类型类概念。参见例如Dreyer,Harper和Chakravarty Modular Type Classes (2007)提供了一个很好的背景故事。

答案 1 :(得分:8)

int listfloat list的唯一常见类型是'a list,即任何类型的列表。由于元素类型可以是任何类型,因此没有可以应用于元素的特定操作。所以没有直接的方法来编写你想要的功能。

如果您愿意将列表与对其元素进行操作的+函数捆绑在一起,则可以通过这种方式解决问题。

let partialsums plus list =
    List.rev
        (List.fold_left
            (fun l n ->
                 if l = [] then [n] else (plus (List.hd l) n) :: l) 
            [] list)

# partialsums (+) [1;3;5;7];;
- : int list = [1; 4; 9; 16]
# partialsums (+.) [1.;3.;5.;7.];;
- : float list = [1.; 4.; 9.; 16.]

在这种情况下,列表元素不必是数字:

# partialsums (^) ["a"; "b"; "c"; "d"];;
- : string list = ["a"; "ab"; "abc"; "abcd"]

另一种常见的解决方案是使用变体类型:

let numlist = Flist of float list | Ilist of int list

liet partialsums (list: numlist) =
    match list with
    | Flist l -> ...
    | Ilist l -> ...

答案 2 :(得分:0)

您也可以像Base所做的那样尝试一流的模块(https://github.com/janestreet/base/blob/57240d0d8403031f37e105351d7d928a6aea1524/src/container.ml#L17),例如:

let sum (type a) ~fold (module M : Commutative_group.S with type t = a) t ~f =
  fold t ~init:M.zero ~f:(fun n a -> M.(+) n (f a))
;;

这给你一个非常轻量级的语法:

List.sum (module Int) [1; 2; 3; 4; 5] ~f:Fn.id