在OCaml中,这是什么类型的定义:'a。 单元
问题
这是我第一次看到类似'a. unit -> 'a
的类型定义'a. unit -> 'a
'a. unit -> 'a
在记录中的显式多态类型
Q1:这是什么'a.
(注意点)?
Q2 :这种类型定义的术语是什么?
如果我做
let f:'a. 'a list -> int = fun l -> List.length l;;
乌托邦表演
val f : 'a list -> int = <fun>
问题3 :为什么不显示类型'a. 'a list -> int
'a. 'a list -> int
?
问题4 :我应该何时使用这种类型定义?
另外,我可以在记录中使用这种定义:
type t = { f: 'a. 'a list -> int};; (* this is correct *)
但我不能在变体中使用它:
type t = Node of ('a. 'a list -> int);; (* this is wrong *)
Q5 :为什么?
更新/总结
我在这个forall type definition
上做了一些实验,因为我无法在网上找到关于OCaml中这个主题的任何文章,并且我想介绍一下它的背后。
我在这里总结这些实验,希望有人可以提供更多的见解。
从下面的答案和它的评论,我觉得'a.
是一种force forall
事物。
1.'a 'a.
在函数定义中
let f:('a -> int) = fun x -> x + 1 (* correct *)
以上是好的,因为OCaml可以自由缩小f的参数类型,并用int
替换'a
。
然而,
let f:'a. ('a -> int) = fun x -> x + 1 (* wrong *)
这不会通过编译器,因为它强制f
通过'a适用于all types
。 。 显然,从定义部分是不可能的,因为x
的唯一可能类型是int
。
这个例子很有趣,因为它展示了OCaml的静态类型推断系统背后的逻辑和魔法。 类型通常从函数定义中自然地显示出来,也就是说,您更关心函数的功能,而不是首先给出类型。
对我来说,真正使用'a.
非常有意义'a.
当定义函数时,就好像函数的定义可以处理所有类型一样,它的类型自然就是'a.
; 如果函数无法处理所有类型,则强制所有类型都没有意义。 我想这是OCaml顶层通常不会显示它的原因之一
2, 'a.
类型推理
let foo f = f [1;2;3] + f [4;5;6] (* correct *)
函数f
将被推断为int list -> int
因为OCaml首先看到[1;2;3]
并且它是一个int list
,所以OCaml假定f
将采用int list
。
这也是为什么下面的代码失败,因为第二个列表是string list
let foo f = f [1;2;3] + f ["1";"2";"3"] (* wrong*)
即使我知道List.length
将是f
一个很好的候选者,OCaml将不会允许由于类型推断系统。
我想如果我强迫f是'a.
,那么f
可以在foo
处理int list
和string list
,所以我做了:
let foo (f:'a. 'a list -> int) = f [1;2;3] + f ["1";"2";"3"];; (* wrong *)
它失败了,OCaml似乎不允许它。 我猜这就是为什么你不能总是在存在意外多态性的情况下进行类型推断,所以OCaml限制它用于记录字段和对象方法。
3.'a 'a.
在记录中
通常我从类型参数中取出'a
:
type 'a a_record = {f: 'a list -> int};; (* correct *)
然而,限制是一旦你申请你得到的具体类型:
let foo t = t.f [1;2;3] + t.f [4;5;6];; (* correct *)
OCaml会将t
推断为int a_record
,而不是'a a_record
。 所以下面会失败:
let foo t = t.f [1;2;3] + t.f ["1";"2";"3"];; (* wrong*)
在这种情况下,我们可以使用'a.
因为OCaml允许它记录类型。
type b_record = {f: 'a. 'a list -> int};; (* correct *)
let foo t = t.f [1;2;3] + t.f ["1";"2";"3"];; (* correct *)
b_record
本身就是一个具体的记录类型,它的f
可以应用于所有类型的列表。 那么我们上面的foo
就会通过OCaml。
'a.
意味着“对于所有类型”a“。 OCaml顶层通常不会显示它,这就是为什么它不会出现在输出中。 任何包含类型变量的表达式在开始时都有一个隐含的表达式,除非在其他地方显示。
定义一个函数时,在开始时添加这个函数可能很有用,以确保类型是多态的。 例如
# let f : ('a -> 'a) = fun x -> x + 1;;
val f : int -> int = <fun>
这里, 'a -> 'a
只是将函数的输出类型限制为与其输入类型相同,但是OCaml可以自由地进一步限制它。 随着'a.
,这不会发生:
# let f : 'a. ('a -> 'a) = fun x -> x + 1;;
Error: This definition has type int -> int which is less general than
'a. 'a -> 'a
你需要指定'a.
当定义记录或对象类型时,您想将类型变量的范围限定为单个成员而不是整个对象。