OCaml中的弱多态性

7

我对OCaml中的弱多态性有些困惑。

请看下面的代码片段,其中我定义了一个函数remember

let remember x =
   let cache = ref None in
      match !cache with
       | Some y -> y
       | None -> cache := Some x; x
;;

编译器可以推断出多态类型 'a -> 'a,并且 cache 在本地使用。
但是当我将上述代码修改为:
let remember =
   let cache = ref None in
    (fun x ->  match !cache with
         | Some y -> y
         | None -> cache := Some x; x)
;;

编译器推断出弱多态类型'_a -> '_a,另外,看起来cacheremember的调用之间是共享的。
为什么编译器会在这里推断出弱多态类型?为什么cache会被共享?
此外,如果我再次更改代码呢?
let remember x =
   let cache = ref None in
    (fun z ->  match !cache with
         | Some y -> z
         | None -> cache := Some x; x)
;;

编译器推断出多态类型 'a -> 'a -> 'acache 变成了局部使用。为什么会这样呢?
3个回答

8
let remember =
 let cache = ref None in
  (fun x ->  match !cache with
       | Some y -> y
       | None -> cache := Some x; x)
;;

在返回的函数中,cache是被闭合的。但是在声明cache的时候,我们并不知道它的类型将会是什么;它将由x的类型以及当remember被定义时创建cache的方式来确定。
但是由于这是一个闭包,我们可以像这样做:
> remember 1
  1

现在很清楚,cache : int option ref已经存储了一些内容。由于只有一个cache,所以remember只能存储一种类型。
在下一个示例中,你需要闭合两个变量,xcache。由于每次调用remember时都会创建一个新的cache引用,因此该类型可以完全多态化。之所以该类型不是弱多态化的,是因为我们知道我们将在其中存储x,并且在创建cache时已经知道了x的类型。

我猜你的意思是 cache 的类型是 int option ref 而不是 ref (None int) - Virgile

6
这似乎与值限制有关。完全的值限制(如SML中)会完全拒绝您的代码。弱多态类型在Jacques Garrigue的论文“Relaxing the Value Restriction”中有描述,我承认在阅读您的问题后偶然发现了它:

http://caml.inria.fr/pub/papers/garrigue-value_restriction-fiwflp04.pdf

如果您对ML代码的正确心理模型有了清晰的认识,那么cache被共享的事实就很明显了。您正在定义两个值:remembercache。嵌套只是将cache的作用域限定为块内部。

1
let remember x =
   let cache = ref None in
      match !cache with
       | Some y -> y
       | None -> cache := Some x; x


let remember x =
   let cache = ref None in
    (fun z ->  match !cache with
         | Some y -> z
         | None -> cache := Some x; x)

在以上两个版本中,remember 是一个“直接”的函数,每次像remember 1这样调用它时,它都会将cache初始化为ref None,是吗?因此实际上它并没有记住任何东西,cache不会在任何remember调用之间共享。
在另一版本中:

let remember =
   let cache = ref None in
    (fun x ->  match !cache with
         | Some y -> y
         | None -> cache := Some x; x)

这是不同的。 remember 仍然是一个函数,但真正定义其内容的部分是 (fun x -> match ...)。它包含了 cache,并且只会初始化一次。因此,cache 在未来的 remember 调用之间是共享的。

你的“其他版本”刚好解决了让我思考了整个昨天下午大部分时间的代码问题! - daruma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接