变体或多态变体?

Fab*_*ant 35 ocaml types programming-languages functional-programming ml

我注意到,在我知道的OCaml程序员中,他们中的一些总是使用多态变体(未声明的变体,以反引号为前缀),而其他变体从不使用多态变体,而更喜欢在类型中声明的变体.

除了性能原因(多态变体目前编译效率低于简单变体),专家OCaml开发人员如何在它们之间进行选择?

Dan*_*zli 39

我的用法可分为以下5类.1.界面2.模块化3.易读性4.简洁5.技巧

  1. 如果变体类型仅在模块内部,我使用常规变体,因为正如您所说,它们的编译效率更高.
  2. 如果在接口中导出变量类型并且我觉得某些情况可能出现在其他模块中但是使它们依赖于模块并不一定有意义,我使用多态变体因为它们不依赖于模块命名空间系统.示例:编码类型类型Xmlm.将信号类型作为变量类型意味着您可以使用相同的XML处理思想开发模块,而不会引入依赖性Xmlm.
  3. 如果在接口中导出变量类型,我发现当变量类型的值被赋予模块的函数时,使用常规变量有时会过于冗长.例如:在版型Uuidm.而不是必须写,Uuidm.create Uuidm.V4你可以简单地写Uuidm.create `V4,这是明确的,不那么冗长.
  4. 有时,特定功能可能会返回不同的情况.如果这些情况仅由此函数使用,我在接口中声明函数类型而不必引入类型定义.例如parse : string -> [`Error of string | `Ok of t]
  5. 多态变体及其子类型允许您使用幻像类型静态强制执行不变量.此外,以增量方式定义它们的可能性非常有用,既可以静态地执行不变量,也可以用于文档目的.

最后,我有时在根据4的模块实现中使用多态变体,但没有它们出现在界面中.除非你声明多态变体并关闭它们,否则我不鼓励这种用法,因为它削弱了静态类型规则.


Mar*_*bon 14

我在大多数模块接口中使用多态变体的唯一原因是解决经典变体的命名问题.

如果以下方法可行,多态变体在大多数情况下将不再有用:

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中有效.欢呼!

  • 为了让那些追随我的人阅读这个关于何时使用多态变体的话题:Martin的例子最终在OCaml 4.01中合法.麾! (3认同)

Ytt*_*ill 12

多态变体总是效率低下并不是真的.使用Martin的例子:

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

使用标准变体执行此操作需要两种不同的类型和完整的重新编码,具有多态变体的基本情况是物理上不变的.当使用开放递归进行术语重写时,此功能真正发挥作用:

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

当重写函数也用开放递归计算时,优点甚至更大.

不幸的是Ocaml的Hindley-Milner类型推断不足以在没有明确输入的情况下做这种事情,这需要仔细分解类型,这反过来使原型打字变得困难.另外,有时需要明确的强制.

这种技术的一大缺点是,对于具有多个参数的术语,很快就会出现相当混乱的类型组合爆炸,最终更容易放弃静态执行并使用带有通配符和异常的厨房接收器类型(即动态打字).