我经常需要声明一个包含地图或列表的类型,例如:
type my_type_1 = my_type_0 IntMap.t
type my_type_2 = my_type_0 List
此外,我还看到了另一种声明形式,它将地图或列表封装在记录中,例如:
type my_type_1 =
| Bot_1
| Nb_1 of my_type_0 IntMap.t
type my_type_2 =
| Bot_2
| Nb_2 of my_type_0 List
我的问题是,是否有些情况下第二种风格是必要的并且比第一种风格更好?
非常感谢!
答案 0 :(得分:2)
由于在第二种情况下添加了Bot
构造函数,因此您提供的两种类型不相同。这意味着两个my_type_1
没有相同的语义。顺便提一下,构造Bot | Foo of 'a
已由标准类型'a option
提供,构造函数为Some
和None
,因此第二个示例的类型my_type_1
是等效的到第一个my_type_1 option
。
是否使用option
类型或您自己的构造函数名称取决于您。一般情况下,如果您的类型的语义与失败,缺席或未定义的option
概念一致,我会建议您使用选项类型。鉴于你的名字Bot
,我认为这可能就是你正在做的事情,但是定义你自己的构造函数名称也没问题,在某些情况下可以更清楚。这个问题已在this blog post from ezyang中深入讨论。
现在,假设您的两个类型定义 等效(即,在没有Bot
)构造函数的情况下,添加代数数据类型层(一个新构造函数)的目的是什么使用简单类型别名?好吧,它具有从表示类型中创建 distinct 类型的效果。例如,如果您定义type 'a stack = Stack of 'a list
,则'a stack
和'a list
不能相互混淆,如果您这样做,编译器将引发错误。这样可以用来强制执行(轻)类型分离,构造函数充当类型注释:
let empty = Stack []
let length (Stack li) = List.length li
我认为这主要是品味问题,但我建议您使用代数数据类型而不是别名,以确保原始类型不会出错。缺点是您必须包装原始数据类型的操作,就像我在上面的length
函数中所做的那样。
答案 1 :(得分:1)
那些样式不同,但不同的类型:第一种类型声明是多态的专用实例(mytype_0
)的缩写{ {1}}或List
。
第二组定义提供了“构造的”类型,IntMap
(和Bot_1
)为其提供了值。例如,可以使用这些“替代”来创建类型Bot_2
的函数,它在计算不允许返回列表的特殊情况下返回T -> my_type_1
,其方式与一个option type允许。对于第一组定义(必须始终提供所需的列表有效负载),这是不可能的。
答案 2 :(得分:0)
第二个不是“记录”(这是另一回事)。它创建了一个代数数据类型。我不知道如何解释它,但如果你使用Haskell或Standard ML,你就会知道。它基本上是一个标记的联合。 my_type_1
一个Bot_1
(不带数据)或Nb_1
(带有my_type_0 IntMap.t
数据)。
第一个只是一个类型同义词(就像C中的typedef)。