OCaml数组是可变的。对于大多数可变类型,即使是“空”值也没有多态类型。
例如,
# ref None;;
- : '_a option ref = {contents = None}
# Hashtbl.create 0;;
- : ('_a, '_b) Hashtbl.t = <abstr>
但是,空数组确实具有多态类型
# [||];;
- : 'a array = [||]
这似乎不可能,因为数组是可变的。
在这种情况下可能会解决问题,因为数组的长度不能改变,因此没有机会打破稳健性。
类型系统中的数组是否特殊,以允许这种情况?
答案 0 :(得分:4)
我不相信。用户定义的数据类型也会出现类似的情况,行为也是一样的。
举个例子,考虑一下:
type 'a t = Empty | One of { mutable contents : 'a }
与数组一样,'a t
是可变的。但是,Empty
构造函数可以像多个空数组一样以多态方式使用:
# let n = Empty in n, n;;
- : 'a t * 'b t = (Empty, Empty)
# let o = One {contents = None};;
val o : '_weak1 option t = One {contents = None}
即使存在'a
类型的值,只要它不在非变量位置,这也有效:
type 'a t = NonMut of 'a | Mut of { mutable contents : 'a }
# let n = NonMut None in n, n;;
- : 'a option t * 'b option t = (NonMut None, NonMut None)
请注意,'a t
的参数仍然是非变量的,并且在将构造函数隐藏在函数或模块中时会丢失多态(大致因为方差将从类型构造函数的参数中推断出来)。
# (fun () -> Empty) ();;
- : '_weak1 t = Empty
与空列表比较:
# (fun () -> []) ();;
- : 'a list = []
答案 1 :(得分:4)
答案很简单 - 空数组具有多态类型,因为它是常量。它是特殊的吗?嗯,有点,主要是因为数组是内置类型,没有表示为ADT,所以是的,在is_nonexpansive函数的typecore.ml
中,有一个数组的情况
| Texp_array [] -> true
然而,这不是一个特例,只是推断哪些句法表达式形成常量。
注意,一般来说,宽松值限制允许泛化非扩展的表达式(不仅仅是经典值限制中的句法常量)。其中非扩展表达式是正常形式的表达式(即常量)或其计算不具有任何可观察副作用的表达式。在我们的例子中,[||]
是一个完美的常数。
OCaml值限制比这更宽松,因为它允许一些扩展表达式的泛化,以防类型变量具有正方差。但这是一个完全不同的故事。
此外,ref None
不是空值。 ref
值本身只是一个带有一个可变字段type 'a ref = {mutable contents : 'a}
的记录,因此它永远不会为空。它包含一个不可变值(或者如果你愿意,引用不可变值)的事实不会使它变为空或多态。与[|None|]
相同,也是非空的。这是一个单身人士。此外,后者具有弱多态类型。