我注意到,在我知道的OCaml程序员中,其中一些总是使用多态变体(未声明的变体,以反引号为前缀),而其他永远不会使用多态变体,并且更喜欢在类型中声明的变体。
除了性能原因(多态变体目前编译效率低于简单变体),专家OCaml开发人员如何在它们之间做出选择?
答案 0 :(得分:36)
我的用法可分为以下5类。 1.界面2.模块化3.易读性4.简洁5.技巧
Xmlm
类型的Xmlm
。同时将encoding type作为变体类型意味着您可以使用相同的XML处理思想开发模块,而不会依赖Uuidm
。Uuidm.create Uuidm.V4
的{{3}}。您可以简单地编写Uuidm.create `V4
,而不必写parse : string -> [`Error of string | `Ok of t]
,这样就可以清晰而简洁。最后,我有时会在模块的实现中使用多态变体,但是没有它们出现在界面中。除非您声明多态变体并关闭它们,否则我不鼓励这种用法,因为它会削弱静态类型规则。
答案 1 :(得分:13)
我在大多数模块接口中使用多态变体的唯一原因是解决经典变体的命名问题。
如果以下方法可行,多数变体在大多数情况下将不再有用:
type t1 = String of string | Int of int | Bool of bool | List of t1 list type t2 = String of string | Int of int | Other let simplify x = match (x : t1) with String s -> String s | Int n -> Int n | Bool _ | List _ -> Other
2014-02-21更新:上面的代码现在在OCaml 4.01中有效。乌拉!
答案 2 :(得分:11)
多态变体总是效率低下并不是真的。用马丁的例子:
type base = [`String of string | `Int of int]
type t1 = [base | `Bool of bool | `List of t1 list]
type t2 = [base | `Other]
let simplify (x:t1):t2 = match x with
| #base as b -> b
| `Bool _ | `List _ -> `Other
使用标准变体执行此操作需要两种不同的类型和完整的重新编码,具有多态变体的基本情况是物理上不变的。当使用开放递归进行术语重写时,这个功能真正发挥作用:
type leaf = [`String of string | `Int of int]
type 'b base = [leaf | `List of 'b list]
type t1 = [t1 base | `Bool of bool ]
type t2 = [t2 base | `Other]
let rec simplify (x:t1):t2 = match x with
| #leaf as x -> x
| `List t -> `List (List.map simplify t)
| `Bool _ -> `Other
并且当重写函数也通过开放递归计算时,优势甚至更大。
不幸的是Ocaml的Hindley-Milner类型推断并不足以在没有明确输入的情况下执行此类操作,这需要仔细分解类型,这反过来会使原型打字变得困难。此外,有时需要明确的强制措施。
这种技术的一个重大缺点是,对于具有多个参数的术语,很快就会出现相当混乱的类型组合爆炸,并且最终更容易放弃静态执行并使用厨房水槽类型通配符和异常(即动态类型)。