这两个功能有什么区别?

时间:2014-02-10 22:46:41

标签: ocaml

我有两个功能:

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?它是什么?

3 个答案:

答案 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 []这样的东西不是值。