我有两个功能:
let rev_flatten l =
List.fold_left (fun acc x -> List.fold_left (fun acc y -> y::acc) acc x) [] l
类型为val rev_flatten : 'a list list -> 'a list = <fun>
和
let rev_flatten =
List.fold_left (fun acc x -> List.fold_left (fun acc y -> y::acc) acc x) []
类型为val rev_flatten : '_a list list -> '_a list = <fun>
我认为它是相同的功能,至少是相同的功能,但为什么它们有两种不同的类型?为什么第二个元素类型为_a
?它是什么?
答案 0 :(得分:4)
以下划线作为前缀的类型变量告诉我们变量是弱多态。弱多态变量只能用于一种类型,但是编译器不能推断出确切的类型,因此类型变量用下划线标记。
当您第一次提供参数时,变量将不再是多态的,并且只能接受单个类型的参数。
通常,函数不是通用的,但如果可能包含可变状态,则标记为弱多态。在您的示例中可能就是这种情况,因为类型系统不知道List.fold_left
是纯函数还是不纯函数。
修改强> 为什么避免部分应用( eta扩展)允许函数(甚至不纯)是多态的?
让我们定义一个具有内部计数器的函数,该函数在每次调用函数时都会递增并打印出来。其中,它将一个函数作为参数,并在增加计数器后应用它:
let count f =
let inc = ref 0 in
(fun x -> inc := !inc + 1; print_int !inc; f x);;
这个函数是多态的:('a -> 'b) -> 'a -> 'b
。
接下来,我们再定义两个函数。每周多态:
let max' = count max;;
val max' : '_a -> '_a -> '_a = <fun>
和多态的:
let max'' x = count max x;;
val max'' : 'a -> 'a -> 'a = <fun>
现在注意执行这些功能时打印的内容:
max' 1 2;; (* prints 1 *)
max' 1 2;; (* prints 2 *)
max' 1 2;; (* prints 3 *)
max'' 1 2;; (* prints 1 *)
max'' 1 2;; (* prints 1 *)
max'' 1 2;; (* prints 1 *)
因此我们设计为每周多态的函数在内部具有持久可变状态,允许按预期使用计数器,而多态函数是无状态并且被重建每次调用,虽然我们想在里面有一个可变变量。
这就是编译器喜欢弱多态函数的原因,该函数可以与任何单一类型一起使用,而不是支持完整的多态。
答案 1 :(得分:2)
类型为'_a list list -> '_a list
的函数是弱多态的。这意味着如果您在int list list
上拨打第二个,rev_flatten
将不再是'_a list list -> 'a list
而是int list list -> int list
您可以在此处详细了解原因的详细信息: http://caml.inria.fr/resources/doc/faq/core.en.html
干杯,
斯科特
答案 2 :(得分:2)
这只是ML风格的价值限制。在以前的SO回答中有一些很好的参考资料:What is the difference between 'a and '_l?。
一般来说,ML系列应用简单的句法测试来确定完全概括是否安全,即使类型完全多态。如果你推广一个不安全的案例,程序会有未定义的行为(它可能会崩溃或得到错误的答案)。所以你只有在安全时才需要这样做。
应用句法规则是因为它(相对)容易记住。一段时间内尝试了一个更为复杂的规则,但它造成的弊大于利(一般的结论)。对ML家族的历史描述将比我能更好地解释它。
您的一个函数(第二个函数)被定义为表达式,即作为函数应用程序。根据价值限制,这不是“安全的”。 (请记住,这只是一个语法测试。)第一个是lambda(fun x - &gt; expr)。这是“安全的”。
它被称为价值限制,因为它认为价值是安全的。函数应用程序不是(语法)值。 lambda是一个句法值。像[]
这样的东西是一个值。像ref []
这样的东西不是值。