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在本地使用。为什么会这样?

4

3 回答 3

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

这里cache被返回的函数关闭。但是在我们声明 的时候cache,我们没有关于类型是什么的信息;它将由定义xcache创建的任何类型决定。remember

但由于这是一个闭包,我们可以这样做:

> remember 1
  1

现在很清楚,cache : int option ref因为我们实际上已经在其中存储了一些东西。由于只有一种cacheremember因此只能存储一种类型。

在下一个中,您关闭了 2 件事,x并且cache. cache因为我们在每次调用类型时创建一个新的ref,remember所以可以再次完全多态。类型不是弱多态的原因是因为我们知道我们将在其中存储,并且在创建时x我们有s 类型。xcache

于 2013-07-29T01:13:58.447 回答
6

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

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

如果您对 ML 代码的含义有一个正确的心理模型,那么跨调用共享的事实cache应该是显而易见的。您正在定义两个值,remembercache。嵌套只是使cache块的私有范围。

于 2013-07-29T01:05:17.060 回答
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是一个“直接”的函数,每次调用 like remember 1,它都会初始化cacheref 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调用之间也是如此。

于 2014-10-11T11:40:51.853 回答