OCaml 中的弱多态性

Posted

技术标签:

【中文标题】OCaml 中的弱多态性【英文标题】:Weak Polymorphism in OCaml 【发布时间】:2013-07-28 16:13:11 【问题描述】:

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

请看下面的sn-p,这里我定义了一个函数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,此外,cache 似乎在remember 的调用之间共享。

为什么编译器在这里推断出弱多态类型,为什么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 成为本地使用的。为什么会这样?

【问题讨论】:

【参考方案1】:
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 因为我们实际上已经在其中存储了一些东西。由于只有一个cacheremember 只能存储一种类型。

在下一个中,你关闭了两个东西,xcache。由于我们在每次调用remember 时创建了一个新的cache ref,因此该类型可以再次完全多态。该类型不是弱多态的原因是因为我们知道我们将在其中存储x,并且在创建cache 时我们有xs 类型。

【讨论】:

我猜你的意思是cache 的类型为int option ref 而不是ref (None int)【参考方案2】:

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

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

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

【讨论】:

【参考方案3】:
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 调用之间共享。

【讨论】:

你的“其他版本”解决了昨天下午大部分时间让我思考的代码!

以上是关于OCaml 中的弱多态性的主要内容,如果未能解决你的问题,请参考以下文章

OCaml 类型系统的精妙之处:多态性、无点反向和嵌套列表

OCaml如何将多态数转换为浮点数?

为什么Ocaml中的“ref None”值受限制?

多态变体和构造函数

内联记录是否包含多态变体?

语言特点