为什么OCaml无法在签名中展开中间参数化类型?
例如:
(* foo.ml *)
type 'a internal = Foo of 'a
type t = string internal
和
(* foo.mli *)
type t = Foo of string
发出错误。
我想这与内存表示有时会有所不同有关,但我想知道在向OCaml错误跟踪器提交错误报告之前是否有更深层次的原因......
答案 0 :(得分:5)
只要类型是结构类型,它就可以,例如:
type 'a t = 'a * int
type u = string t
将匹配
type u = string * int
但是,变体(和记录)是OCaml中的名义类型。也就是说,这种类型的每个声明都引入了一个新的类型名称。签名中的名义类型规范只能与名义类型声明匹配,并且它们必须具有等效定义。在您的示例中也不是这种情况,因此不被接受。 (这是OCaml的一个微妙角落。结构类型别名和名义类型定义共享相同语法的事实无济于事。)
FWIW,你也可以重新标识名义类型:
type 'a t = Foo of 'a
type 'a u = 'a t = Foo of 'a
将匹配
type 'a u = Foo of 'a
但是这也不允许你改变结构或参数,所以对你的情况没有帮助。
答案 1 :(得分:4)
这不是内存表示问题。在对类型签名匹配类型声明时,或者更一般地,在检查声明t1
是否不如声明t2
更通用时,类型检查器当前只考虑这三种情况:
t2
是抽象类型或类型缩写t1
和t2
都是和类型t1
和t2
都是记录其他情况失败并出现错误。在您的情况下,t1
(要检查的类型)是类型缩写,t2
(规范)是和类型。这会因类型错误而失败。
请参阅typing/includemod.ml中的源代码:type_declarations
。
这不是内存表示的考虑因素,因为foo.ml
也会失败:
type u = Foo of string
type t = u
也许这个检查可以改进。你应该问bugtracker。
编辑:判断这项检查应该改进多远并非易事。检查签名匹配时,在签名方面扩展缩写通常是不正确的,例如,不应接受以下内容:
module Test : sig
type t = Foo
type u = t (* to the outside, t and u would be equal *)
end = struct
type t = Foo (* while internally they are different *)
type u = Foo (* sum/records are generative/nominative *)
end
另一种方式(内部平等从外部隐藏)是正确的,并且已经可能:
module Test : sig
type t = Foo
type u = Foo
end = struct
type t = Foo
type u = t = Foo
end;;
fun (x : Test.t) -> (x : Test.u);;
(* Error: This expression has type Test.t but an expression
was expected of type Test.u *)
现在,在考虑缩写扩展时也会考虑内存表示,因为类型系统的动态语义(内存表示选择)不会被这种扩展保留:
module Test : sig
type r = { x : float; y : float; z : float } (* boxed float record *)
end = struct
type 'a t = { x : 'a; y : 'a; z : 'a } (* polymorphic record *)
type r = float t
end