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

Dou*_*wit 8 polymorphism ocaml

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

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

Sim*_*ine 13

使用参数多态,没有.使用ad-hoc多态,是的.

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

module type Semigroup = sig
  type t
  val add : t -> t -> t
end
Run Code Online (Sandbox Code Playgroud)

和一些实用函数一样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
Run Code Online (Sandbox Code Playgroud)

你可以得到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]
Run Code Online (Sandbox Code Playgroud)

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

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

使用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
Run Code Online (Sandbox Code Playgroud)

这将允许定义泛型重载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))
Run Code Online (Sandbox Code Playgroud)

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

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"]
Run Code Online (Sandbox Code Playgroud)

显然已经有几次尝试统一ML模块系统和Haskell的类型类概念.参见例如Dreyer,Harper和Chakravarty的Modular Type Classes(2007),以获得良好的背景故事.


Jef*_*eld 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.]
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

liet partialsums (list: numlist) =
    match list with
    | Flist l -> ...
    | Ilist l -> ...
Run Code Online (Sandbox Code Playgroud)


neo*_*neo 5

您还可以像 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))
;;
Run Code Online (Sandbox Code Playgroud)

这给了你一个相当轻量级的语法:

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

  • 这是迄今为止最好的答案,因为它依赖于现有的库和可用的语法。:) 如果新手不清楚,[Base](https://opensource.janestreet.com/base/) 是 Jane Street 的替代标准库的名称。它需要使用包管理器,并且在理解参数化模块方面确实有一点额外的分量。但他们还围绕它编写了一本书,即《Real World OCaml》第二版](https://realworldocaml.org/),该书已经“进行中”一段时间了,但绝对不是空的。 (2认同)