简体   繁体   English

为什么OCaml有时需要进行eta扩展?

[英]Why does OCaml sometimes require eta expansion?

If I have the following OCaml function: 如果我有以下OCaml功能:

let myFun = CCVector.map ((+) 1);;

It works fine in Utop, and Merlin doesn't mark it as a compilation error. 它在Utop中运行良好,Merlin不会将其标记为编译错误。 When I try to compile it, however, I get the following error: 但是,当我尝试编译它时,我收到以下错误:

Error: The type of this expression, (int, '_a) CCVector.t -> (int, '_b) CCVector.t , contains type variables that cannot be generalized 错误:此表达式的类型, (int,'_ a)CCVector.t - >(int,'_ b)CCVector.t ,包含无法泛化的类型变量

If I eta-expand it however then it compiles fine: 如果我eta-expand它然后它编译好:

let myFun foo = CCVector.map ((+) 1) foo;;

So I was wondering why it doesn't compile in eta-reduced form, and also why the eta-reduced form seems to work in the toplevel (Utop) but not when compiling? 所以我想知道为什么它不能以eta-reduced形式编译,以及为什么eta-reduced表单似乎在顶层(Utop)中工作但在编译时不起作用?

Oh, and the documentation for CCVector is here . 哦,CCVector的文档就在这里 The '_a part can be either `RO or `RW, depending whether it is read-only or mutable. '_a部分可以是`RO或`RW,具体取决于它是只读还是可变。

What you got here is the value polymorphism restriction of ML language family. 你在这里得到的是ML语言家族的价值多态性限制。

The aim of the restriction is to settle down let-polymorphism and side effects together. 限制的目的是将let-polymorphism和副作用结合起来。 For example, in the following definition: 例如,在以下定义中:

let r = ref None

r cannot have a polymorphic type 'a option ref . r不能有多态类型'a option ref Otherwise: 除此以外:

let () =
  r := Some 1;       (* use r as int option ref *)
  match !r with
  | Some s -> print_string s  (* this time, use r as a different type, string option ref *)
  | None -> ()

is wrongly type-checked as valid, but it crashes, since the reference cell r is used for these two incompatible types. 错误地类型检查为有效,但它崩溃了,因为参考单元r用于这两种不兼容的类型。

To fix this issue many researches were done in 80's, and the value polymoprhism is one of them. 为了解决这个问题,在80年代进行了许多研究,价值多态性就是其中之一。 It restricts polymorphism only to let bindings whose definition form is "non-expansive". 它将多态性限制为只允许定义形式为“非扩展”的绑定。 Eta expanded form is non expansive therefore your eta expanded version of myFun has a polymorphic type, but not for eta reduced one. Eta扩展形式是非扩展的,因此你的eta扩展版本的myFun具有多态类型,但不是eta减少一个。 (More precisely speaking, OCaml uses a relaxed version of this value polymorphism, but the story is basically the same.) (更确切地说,OCaml使用这个值多态的宽松版本,但故事基本相同。)

When the definition of let binding is expansive there is no polymorphism introduced therefore the type variables are left non-generalized. 当let绑定的定义是扩展的时,没有引入多态,因此类型变量是非泛化的。 These types are printed as '_a in the toplevel, and their intuitive meaning is: they must be instantiated to some concrete type later: 这些类型在顶层打印为'_a ,它们的直观含义是:它们必须在以后实例化为某种具体类型:

# let r = ref None                           (* expansive *)
val r : '_a option ref = {contents = None}   (* no polymorphism is allowed *)
                                             (* type checker does not reject this,
                                                hoping '_a is instantiated later. *)

We can fix the type '_a after the definition: 我们可以在定义后修复类型'_a

# r := Some 1;;                              (* fixing '_a to int *)
- : unit = ()
# r;;
- : int option ref = {contents = Some 1}     (* Now '_a is unified with int *)

Once fixed, you cannot change the type, which prevents the crash above. 修复后,您无法更改类型,从而防止上面的崩溃。

This typing delay is permitted until the end of the typing of the compilation unit. 允许此输入延迟,直到编译单元的输入结束。 The toplevel is a unit which never ends and therefore you can have values with '_a type variables anywhere of the session. toplevel是一个永不结束的单元,因此你可以在会话的任何地方使用'_a类型变量。 But in the separated compilation, '_a variables must be instantiated to some type without type variables till the end of ml file: 但是在分离的编译中, '_a变量必须在没有类型变量的情况下实例化为某种类型,直到ml文件结束:

(* test.ml *)
let r = ref None (* r : '_a option ref *)
(* end of test.ml. Typing fails due to the non generalizable type variable remains. *)

This is what is happening with your myFun function with the compiler. 这是你的myFun函数与编译器一起发生的事情。

AFAIK, there is no perfect solution to the problem of polymorphism and side effects. AFAIK,对于多态性和副作用的问题没有完美的解决方案。 Like other solutions, the value polymorphism restriction has its own drawback: if you want to have a polymorphic value, you must make the definition in non-expansive: you must eta-expand myFun . 与其他解决方案一样,值多态限制也有其自身的缺点:如果您想要具有多态值,则必须以非扩展方式进行定义:您必须eta-expand myFun This is a bit lousy but is considered acceptable. 这有点糟糕但被认为是可以接受的。

You can read some other answers: 你可以阅读其他一些答案:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM