我可以用什么方法来完成Haskell的类型类在OCaml中的作用?基本上,我想编写一个多态函数而不需要编写太多代码。进行多态的典型方法是提供一个额外的参数,告诉函数它当前正在处理什么类型。例如,假设我想对一个int列表进行排序,我必须将另一个比较器传递给该函数。
type comparison = Lesser | Equal | Greater
my_sort : (a' -> a' -> comparison) -> 'a list -> 'a list
有没有告诉OCaml我的类型是否具有可比性而没有为我想要排序的每种类型编写比较器函数?这意味着我的排序功能看起来像这样:
my_sort : 'a list -> 'a list
答案 0 :(得分:25)
这实际上取决于你想要达到的目标。
如果您对OCaml多态比较功能感到满意(它不适用于循环和功能值),您可以简单地写一下:
let my_sort l = List.sort Pervasives.compare l
模仿类型类的更通用的方法是使用仿函数:
module type COMPARABLE = sig
type t
val compare: t -> t -> int
end
module MySort (C: COMPARABLE) = struct
let sort l = List.sort C.compare l
end
(* You can now use instantiate the functor *)
module IntAscending = struct
type t = int
let compare = (-)
end
module IntDescending = struct
type t = int
let compare x y = y - x (* Reverse order *)
end
module SortAsc = MySort(IntAscending)
module SortDesc = MySort(IntDescending)
答案 1 :(得分:22)
" Modular Type Classes"详细说明了这一点。由Derek Dreyer,Robert Harper和Manuel M. T. Chakravarty撰写。在第34届ACM SIGPLAN会议论文集 - SIGP编程语言原理研讨会上,ACM出版社,2007年。摘自:
ML模块和Haskell类型类已被证明是程序结构的高效工具。模块强调程序组件的显式配置和数据抽象的使用。类型类强调隐式程序构造和ad hoc多态。在本文中,我们展示了如何通过将类型类视为模块的特定使用模式,在显式类型模块语言的框架内支持类型类编程的隐式类型样式。此视图提供了模块和类型类的和谐集成,其中类型类功能(如类层次结构和关联类型)自然地作为现有模块语言结构(如模块层次结构和类型组件)的使用而出现。此外,程序员可以明确控制哪些类型类实例可供给定范围内的类型推断使用。我们将我们的方法形式化为Harper-Stone风格的细化关系,并提供声音类型推理算法作为实现的指南。
答案 2 :(得分:11)
我遇到了一篇非常好的文章,演示了Haskell中的类型和类型类以及OCaml中的模块和模块类型之间的转换:http://conway.rutgers.edu/~ccshan/wiki/blog/posts/Translations/
基本上,正如@Thomas所示,Haskell中的类型类变为OCaml中的模块类型,其类型(实现类型类的类型)和使用该类型的一堆值。
然后,对应于Haskell中类型类的“实例”,您在OCaml中有一个实现模块类型的模块,类型是实例的类型,值是实现的值类型类。
然后,每当你有一个“使用”Haskell中受该类型类约束的类型的函数时,你需要将该函数包装在OCaml中的模块中。这个模块(实际上是一个仿函数)将采用一个参数模块,该模块对应于我们正在使用的类型类的实例。
每次使用该函数时,您都需要首先使用该仿函数创建一个合适的模块,并将其传递给类型类的正确实例。
你会注意到Haskell和OCaml这样做的一个很大的不同,就是在Haskell中,当你使用最后一个函数时,编译器推断类型类的正确实例您;而使用OCaml方式,用户必须明确指定要使用的实例。
答案 3 :(得分:8)
虽然没有像模块那样接近Haskell,但是对象类层次结构的class types没有明确解释。
更新:工作示例:
type comparison = Lesser | Equal | Greater
class type comparable = object ('a)
method compareTo: 'a -> comparison
end ;;
class type textualizable = object
method toString: string
end ;;
(* this corresponds in Haskell to a multiparameter type class *)
class type ['b] printable = object ('a)
constraint 'b = #textualizable
method printWithPrefix: 'b -> unit
end ;;
class type ['b] comparableAndPrintable = object ('a)
inherit comparable
inherit ['b] printable
end ;;
(* -------------- *)
class textile (str_init:string): textualizable = object
val str = str_init
method toString = str
end ;;
class comparableAndPrintableImpl1 (x_init: int) = object (this:'a)
constraint 'a = 'b #comparableAndPrintable (* interface implementation requirement *)
constraint 'b = textualizable (* concrete type parameter *)
val x = x_init
method getx = x
method compareTo (that:'a) = let r = this#getx - that#getx in
match r with
| 0 -> Equal
| _ when r < 0 -> Lesser
| _ -> Greater
method printWithPrefix (pref: 'b) = Printf.printf "%s %d\n" pref#toString x
end ;;
let boxSort (pivot: #comparable) (lows, equals, highs) (x: #comparable) =
match x#compareTo pivot with
| Lesser -> x :: lows, equals, highs
| Equal -> lows, x :: equals, highs
| Greater -> lows, equals, x :: highs
;;
let rec qsort (li : #comparable list) =
match li with
[] | [_] -> li
| [a;b] -> (match a#compareTo b with
Lesser | Equal -> [a;b]
| Greater -> [b;a]
)
| x :: xs -> let (lows, equals, highs) = List.fold_left (boxSort x) ([], [], []) xs in
qsort lows @ (x :: equals) @ qsort highs
;;
let print_myList (prefix: 'a) (li: 'a #printable list) =
let print_it it = it#printWithPrefix prefix in
print_endline "\nlist: " ;
List.iter print_it li
;;
let intlist (lfrom: int) (lto: int) =
let open BatLazyList in
to_list (range lfrom lto) (* lazy range generator from BatLazyList *)
;;
let myComparableAndPrintableList =
List.map (new comparableAndPrintableImpl1) (List.rev (intlist 1 5))
;;
let myprefix = new textile "x ="
let sortAndPrint (li: 'a #comparableAndPrintable list) =
let sorted = qsort li in
print_myList myprefix li ;
print_myList myprefix sorted
;;
sortAndPrint myComparableAndPrintableList ;;
编译并链接:
ocamlfind ocamlc -package batteries -linkpkg test.ml -o test
答案 4 :(得分:1)
通常,由于OCaml不支持隐式参数,因此需要某种字典参数来显式传入类型类实例。这可以根据多态记录,第一类模块或对象来实现。我有一个示例项目展示了使用模块的方式:https://github.com/hongchangwu/ocaml-type-classes