在OCaml中,我想定义一个函数f
,它接受input
来更新记录x
。在以下两种方法中,我很感兴趣一种方法是否优于另一种方法(可读性除外)。
变体方法
type input =
| A of int
| B of string
let f x = function
| A a -> { x with a }
| B b -> { x with b }
GADT方法
type _ input =
| A : int input
| B : string input
let f (type t) x (i: t input) (v: t) =
match i with
| A -> { x with a = v }
| B -> { x with b = v }
答案 0 :(得分:2)
ADT专业人士:
string -> input
类型的函数非常简单。GADT专业人士:
更准确地说,GADT版本可以看作是ADT版本的分解。您可以系统地将一个转换为另一个,并且内存布局将类似(借助于小注释):
type a and b and c
type sum =
| A of a
| B of b
| C of c
type _ tag =
| A : a tag
| B : b tag
| C : c tag
type deppair = Pair : ('a tag * 'a) -> deppair [@@ocaml.unboxed]
let pack (type x) (tag : x tag) (x : x) = Pair (tag, x)
let to_sum (Pair (tag, v)) : sum = match tag with
| A -> A v
| B -> B v
| C -> C v
let of_sum : sum -> deppair = function
| A x -> pack A x
| B x -> pack B x
| C x -> pack C x
答案 1 :(得分:0)
正如您注意到GADT的(非)可读性是一个很大的缺点。尽可能避免GADT。更容易打字和更容易阅读。不太复杂的错误消息。
在运行时简化它们是相同的。它们表示为带有标记和字段的简单整数或块,代码使用标记进行匹配和分支。所以两者都没有给你带来优势。
在编译时,GADT更强大,因为编译器可以用ADT不允许的方式检查类型。一个例子是存在类型,如另一个答案中的例子。因此,当您无法使用ADT时,请使用GADT。