type t = MyInt of int | MyFloat of float | MyString of string
let foo printerf = function
| MyInt i -> printerf string_of_int i
| MyFloat x -> printerf string_of_float x
| MyString s -> printerf (fun x -> x) s
Run Code Online (Sandbox Code Playgroud)
它报告:
Error: This expression has type float -> string
but an expression was expected of type int -> string
Run Code Online (Sandbox Code Playgroud)
移植这个确切的代码片段(我猜,它来自这个网页)的正确方法是使用这个FAQ条目中解释的第一类多态:如何编写具有多态参数的函数?.请阅读FAQ条目,但是为了快速参考,这里有一个工作代码示例:
type t = MyInt of int | MyFloat of float | MyString of string
type 'b printer = { print : 'a . ('a -> string) -> 'a -> 'b }
let foo erf = function
| MyInt i -> erf.print string_of_int i
| MyFloat x -> erf.print string_of_float x
| MyString s -> erf.print (fun x -> x) s
let () = foo
{ print = fun printer data -> print_endline (printer data) }
(MyString "Hello World!")
Run Code Online (Sandbox Code Playgroud)
但请注意,这里实际上并不需要这种一流的多态性.通过参数化,printer对类型数据唯一能做的'a就是将其传递给打印功能
'a -> string.因此,打印机的行为完全取决于它对结果string数据的作用.换句话说,类型与类型'b printer同构string -> 'b,它没有带来其他信息.所以你可以写:
let foo erf = function
| MyInt i -> erf (string_of_int i)
| MyFloat x -> erf (string_of_float x)
| MyString s -> erf s
let () = foo print_endline (MyString "Hello World!")
Run Code Online (Sandbox Code Playgroud)
该类型的foo现在(string -> 'a) -> t -> 'a.这被称为
延续传递样式:获得'a这种类型的唯一方法是在字符串上调用参数函数,所以实际上你只是返回一个字符串,并调用该函数(继续)在上面.这可以改写为:
let foo = function
| MyInt i -> string_of_int i
| MyFloat x -> string_of_float x
| MyString s -> s
let () = print_endline (foo (MyString "Hello World!"))
Run Code Online (Sandbox Code Playgroud)
长话短说:你所展示的代码过于复杂,而且看起来造成的问题被夸大了.这里不需要复杂的类型系统.(但你肯定可以找到更好的例子,其中FAQ中所展示的一流多态性实际上是有用的.只有这个例子......很糟糕.)
这个问题所要求的一流多态性是历史性的.
'a . ('a -> string) -> 'a -> 'b意思是"为所有人'a,('a - >字符串) - >'a - >'b".它是类型变量中的多态的类型('a -> string) -> 'a -> 'b(由'a位"定义" ),并且具有自由变量'a .(定义为该'b类型的参数).
printer上面代码的第一个版本中的类型是foo.可以理解为编码System F类型
forall 'b . (forall 'a . ('a -> string) -> 'a -> 'b) -> t -> 'b
Run Code Online (Sandbox Code Playgroud)
ML类型推断算法推断类型限于"前缀多态",即所有类型变量在前面量化的类型.这是上述'b printer -> t -> 'b类型的情况,但不是在(多态)参数内'b量化的情况.类型系统完全尊重ML(Hindley-Damas-Milner)推理系统的这种限制,不会推断出这种类型,因此拒绝需要它作为错误类型的程序(意思不是"错误的",而是"我无法证明它是对的") ).
这种限制使得推理系统都是可判定的(完整系统F的类型推断是不可判定的(Joe B. Wells))和"主体"(如果你不知道什么是主要类型,请参阅这个SO讨论 ;简而言之,它意味着推理引擎总是选择最通用的类型),但它也拒绝类型很好的程序,这是安全类型系统的常见祸害.
大多数ML语言(OCaml,Haskell ......)在某些时候遇到了他们真正希望能够编写的代码的这种限制.对于九十年代的OCaml,在编写语言中的面向对象编程模式时(参见手册中的示例).
这就是为什么第一类多态首先在方法类型中引入,然后扩展到记录(一级模块是一个独立的,更新近的添加,也允许这个).有一个很好的理由为什么它或多或少地局限于某些意义上具有"字段"的"事物",因为这提供了一种自然的方式来放置(必要的)多态性注释,而字段投影是一个很好的地方. type-checker测试实例化多态值的必要性 - 如果你愿意,这会使理论变得更简单.
对于OCaml社区这一时期的研究工作,请参见例如使用半显式高阶多态性扩展ML,Jacques Garrigue和DidierRémy,1997.已经开发了其他一流多态的方法(参见第5节) )Dider Le Botlan的重铸MLF概述了文献),并且发现了其他用例,特别是'aHaskell中的monad(Lazy Functional State Threads,Simon Peyton Jones和John Launchbury,1994).