包含大量字段的变量开始变得笨拙。每个字段的作用以注释结束:
type ty =
| FunTy of (*all*) ty list * (*params*) ty list * (*return type*) ty
...
一个解决方案是让变量的值是一个记录。
type ty =
| FunTy of ft
...
and ft = {
forall : ty list;
p_tys : ty list;
ret_ty : ty
}
但是,需要间接地取消对记录字段的引用。这与我的目的无关,但我可以看到这种性能差异使上述重构在实践中变得独特。
为了保持答案的客观性,参考OCaml文档或风格指南会有所帮助。
自OCaml 4.03(2016年发布)以来,您可以使用内联记录,它为您提供了记录语法的良好接口 - plus,您可以拥有可变字段 - 与元组语法具有相同的内存表示。
type ty =
| FunTy of {
forall : ty list;
p_tys : ty list;
ret_ty : ty
}
...
您有一些工具来操作包含的记录,但是(与元组语法中的参数" tuple "没有什么不同)它不是一级值(请参阅上面链接的文档了解更多详细信息)。
这是一个风格问题,所以我会记下我的看法。
在这个特殊的例子中,我们可以选择使用元组指定Variant构造函数还是使用record,即使我认为对于太多的字段,也应该使用record。
模式匹配无论如何都是足够的,无论是元组还是记录。
无论是元组还是记录,在处理值时都必须指定所有字段。
然而,真正的优势来自于处理太多字段的记录,如
- 记录字段可以通过键来识别,这样管理起来更方便,并且字段以顺序无关的方式处理,这意味着作者可以更好地控制。
- 元组字段是顺序显著的,这意味着作者在处理元组数据 时必须小心。
虽然元组在内存方面会更小,但是当字段数量增加时,记录确实有优势。
此意见严格针对元组或记录。任何其他组合,可能会遵循不同的意见…从我。: -)
当您看到构造函数接受许多参数的变体类型时,您可能希望考虑以另一种方式使用更多的间接方式。考虑一个简单的、人为的例子,指定一个类型来构建一个三角形。我们需要在2D或3D空间中存储每个角的坐标。
type triangle =
Triangle_2d of float * float * (* Point A *)
float * float * (* Point B *)
float * float (* Point C *)
| Triangle_3d of float * float * float * (* Point A *)
float * float * float * (* Point B *)
float * float * float (* Point C *)
谈论不舒服。所以我们用records
type triangle =
Triangle_2d of { ax : float; ay : float;
bx : float; by : float;
cx : float; cy : float }
| Triangle_3d of { ax : float; ay : float; az : float;
bx : float; by : float; bz : float;
cx : float; cy : float; cz : float }
但是当我们真正思考它时,我们不只是指定坐标吗?也许我们应该创建一个坐标类型
type coordinate =
Coord_2d of { x : float; y : float }
| Coord_3d of { x : float; y : float; z : float }
那么我们的三角形类型就是:
type triangle =
Triangle_2d of { a : coord; b : coord }
| Triangle_3d of { a : coord; b : coord; c : coord }
甚至:
type triangle =
Triangle_2d of coord * coord
| Triangle_3d of coord * coord * coord
它看起来确实干净多了,但它也让我们把问题分解成更小的部分。如果我想计算三角形上两个顶点之间的距离,我不需要对整个三角形做数学运算。我可以简单地定义一个函数来处理坐标,我可以在这个层次上处理二维和三维坐标的区别。我甚至可以将其分解并编写将3D坐标投影到给定轴或平面上的函数。