变体与 GADT 方法
Variant vs GADT approach
在 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 }
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
正如您所注意到的,GADT 的(非)可读性是一个很大的缺点。尽可能避免使用 GADT。更容易打字,更容易阅读。错误消息也不太复杂。
在运行时简化它们是相同的。它们表示为带有标签和字段的简单整数或块,并且代码使用标签来匹配和分支。所以两者都不给你优势。
在编译时,GADT 更加强大,因为编译器可以以 ADT 不允许的方式检查类型。一个例子是存在类型,就像另一个答案中的例子一样。所以不能用ADT的时候就用GADT。
在 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 }
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
正如您所注意到的,GADT 的(非)可读性是一个很大的缺点。尽可能避免使用 GADT。更容易打字,更容易阅读。错误消息也不太复杂。
在运行时简化它们是相同的。它们表示为带有标签和字段的简单整数或块,并且代码使用标签来匹配和分支。所以两者都不给你优势。
在编译时,GADT 更加强大,因为编译器可以以 ADT 不允许的方式检查类型。一个例子是存在类型,就像另一个答案中的例子一样。所以不能用ADT的时候就用GADT。