如何通过类型构造函数比较相等值?
How do I compare values for equality by Type Constructor?
背景
我是 Reason 的新手,比较带参数的变体如此简单让我惊喜不已:
type t = Header | Int(int) | String(string) | Ints(list(int)) | Strings(list(string)) | Footer;
比较不同的变体很好而且可以预测:
/* not equal */
Header == Footer
Int(1) == Footer
Int(1) == Int(2)
/* equal */
Int(1) == Int(1)
这甚至适用于复杂类型:
/* equal */
Strings(["Hello", "World"]) == Strings(["Hello", "World"])
/* not equal */
Strings(["Hello", "World"]) == Strings(["a", "b"])
问题
是否可以仅通过我无法找到的现有内置 operator/function 或其他一些语言构造来比较类型构造函数?
let a = String("a");
let b = String("b");
/* not equal */
a == b
/* for sake of argument, I want to consider all `String(_)` equal, but how? */
可以通过检查值的内部表示来实现,但我不建议这样做,因为它相当脆弱,而且我不确定编译器版本和各种 back-ends诸如此类的内部结构。相反,我建议要么编写 hand-built 函数,要么使用一些 ppx 生成您手写的相同类型的代码。
但这并不好玩,所以话虽这么说,这应该做你想做的,使用几乎没有记录的 Obj
module:
let equal_tag = (a: 'a, b: 'a) => {
let a = Obj.repr(a);
let b = Obj.repr(b);
switch (Obj.is_block(a), Obj.is_block(b)) {
| (true, true) => Obj.tag(a) == Obj.tag(b)
| (false, false) => a == b
| _ => false
};
};
哪里
equal_tag(Header, Footer) == false;
equal_tag(Header, Int(1)) == false;
equal_tag(String("a"), String("b")) == true;
equal_tag(Int(0), Int(0)) == true;
要了解此函数的工作原理,您需要了解 OCaml 在内部如何表示值。这在关于 Representation of OCaml data types in the OCaml manual's chapter on Interfacing C with OCaml 的部分中有所描述(例如,我们已经在这里看到了这可能不适用于各种 JavaScript back-ends 的迹象,尽管我相信至少现在是这样. 我用 BuckleScript/rescript 测试过这个,js_of_ocaml 倾向于更接近内部结构。)
具体来说,这部分关于变体的表示如下:
type t =
| A (* First constant constructor -> integer "Val_int(0)" *)
| B of string (* First non-constant constructor -> block with tag 0 *)
| C (* Second constant constructor -> integer "Val_int(1)" *)
| D of bool (* Second non-constant constructor -> block with tag 1 *)
| E of t * t (* Third non-constant constructor -> block with tag 2 *)
也就是说,没有有效载荷的构造函数直接表示为整数,而有有效载荷的构造函数则表示为带有标签的“块”。还要注意 block 和 non-block 标签是独立的,所以我们不能首先从我们然后比较的值中提取一些“通用”标签值。相反,我们必须检查它们是否都是块,然后然后比较它们的标签。
最后,请注意,虽然此函数可以接受任何类型的值,但它在编写时仅考虑了变体。比较其他类型的值可能会产生意想不到的结果。这是不使用它的另一个很好的理由。
背景
我是 Reason 的新手,比较带参数的变体如此简单让我惊喜不已:
type t = Header | Int(int) | String(string) | Ints(list(int)) | Strings(list(string)) | Footer;
比较不同的变体很好而且可以预测:
/* not equal */
Header == Footer
Int(1) == Footer
Int(1) == Int(2)
/* equal */
Int(1) == Int(1)
这甚至适用于复杂类型:
/* equal */
Strings(["Hello", "World"]) == Strings(["Hello", "World"])
/* not equal */
Strings(["Hello", "World"]) == Strings(["a", "b"])
问题
是否可以仅通过我无法找到的现有内置 operator/function 或其他一些语言构造来比较类型构造函数?
let a = String("a");
let b = String("b");
/* not equal */
a == b
/* for sake of argument, I want to consider all `String(_)` equal, but how? */
可以通过检查值的内部表示来实现,但我不建议这样做,因为它相当脆弱,而且我不确定编译器版本和各种 back-ends诸如此类的内部结构。相反,我建议要么编写 hand-built 函数,要么使用一些 ppx 生成您手写的相同类型的代码。
但这并不好玩,所以话虽这么说,这应该做你想做的,使用几乎没有记录的 Obj
module:
let equal_tag = (a: 'a, b: 'a) => {
let a = Obj.repr(a);
let b = Obj.repr(b);
switch (Obj.is_block(a), Obj.is_block(b)) {
| (true, true) => Obj.tag(a) == Obj.tag(b)
| (false, false) => a == b
| _ => false
};
};
哪里
equal_tag(Header, Footer) == false;
equal_tag(Header, Int(1)) == false;
equal_tag(String("a"), String("b")) == true;
equal_tag(Int(0), Int(0)) == true;
要了解此函数的工作原理,您需要了解 OCaml 在内部如何表示值。这在关于 Representation of OCaml data types in the OCaml manual's chapter on Interfacing C with OCaml 的部分中有所描述(例如,我们已经在这里看到了这可能不适用于各种 JavaScript back-ends 的迹象,尽管我相信至少现在是这样. 我用 BuckleScript/rescript 测试过这个,js_of_ocaml 倾向于更接近内部结构。)
具体来说,这部分关于变体的表示如下:
type t =
| A (* First constant constructor -> integer "Val_int(0)" *)
| B of string (* First non-constant constructor -> block with tag 0 *)
| C (* Second constant constructor -> integer "Val_int(1)" *)
| D of bool (* Second non-constant constructor -> block with tag 1 *)
| E of t * t (* Third non-constant constructor -> block with tag 2 *)
也就是说,没有有效载荷的构造函数直接表示为整数,而有有效载荷的构造函数则表示为带有标签的“块”。还要注意 block 和 non-block 标签是独立的,所以我们不能首先从我们然后比较的值中提取一些“通用”标签值。相反,我们必须检查它们是否都是块,然后然后比较它们的标签。
最后,请注意,虽然此函数可以接受任何类型的值,但它在编写时仅考虑了变体。比较其他类型的值可能会产生意想不到的结果。这是不使用它的另一个很好的理由。