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
它报告:
Error: This expression has type float -> string
but an expression was expected of type int -> string
最佳答案
移植此确切代码段的正确方法(我想这是
来自this
webpage的)
一流的多态性,如以下常见问题解答条目中所述:How to write
a function with polymorphic
arguments?。请
请阅读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!")
但是请注意,您实际上并不需要这个一流的
多态性在这里。根据参数,
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!")
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!"))
长话短说:您显示的代码过于复杂,并且
它似乎构成的问题被夸大了。不需要
这里是一个复杂的类型系统。 (但您当然可以找到更好的选择
常见问题解答中展示的一流多态性的示例是
实际上有用。只是这个例子很烂...)
历史和科学背景
问题所要求的关于一流多态性的历史性保留。
'a . ('a -> string) -> 'a -> 'b
表示“对于所有'a
,('a -> string) -> 'a -> 'b
”。它是在类型变量'a
(由'a .
位“定义”)中多态的类型,并具有自由变量'b
(定义为printer
类型的参数) 。上面代码的第一版中
foo
的类型是'b printer -> t -> 'b
。可以理解为对System F类型进行编码forall 'b . (forall 'a . ('a -> string) -> 'a -> 'b) -> t -> 'b
ML类型推断算法是将类型推断为仅限于“前缀多态性”,即所有类型变量都在前面进行量化的类型。上面类型中的
'b
就是这种情况,但在(多态)参数中量化的'a
则不是这种情况。严格遵守ML(Hindley-Damas-Milner)推理系统的这种限制的类型系统不会推断该类型,因此会拒绝需要将其视为错误类型的程序(这不是“错误”,而是“我无法证明它是正确的”) )。这种限制使得推理系统既可以判定(整个系统F的类型推理是不可判定的(Joe B. Wells)),也可以是“主要的”(如果您不知道什么是主体类型,请参见this SO discussion;简而言之,这意味着推理引擎总是选择最通用的类型),但它也拒绝类型正确的程序,这是安全类型系统的常见祸根。
大多数ML语言(OCaml,Haskell ...)在某种程度上都遇到了他们真正希望能够编写的代码的限制。对于90年代的OCaml,当使用该语言对面向对象的编程模式进行编码时(请参见examples in the manual)。
这就是为什么在方法类型中首先引入一流的多态性,然后将其扩展到记录的原因(一流的模块是独立的,并且是最近才添加的模块,它也允许这样做)。有一个很好的理由说明,在某种意义上为什么这仍然或多或少地局限于具有“字段”的“事物”,因为这为放置(必要的)多态注释提供了一种自然的方式,而字段投影对于类型检查器来测试实例化多态值的需要-如果可以的话,这会使理论更简单。
有关OCaml社区在此期间的研究工作,请参见例如Extending ML with Semi-Explicit Higher-Order Polymorphism,Jacques Garrigue和DidierRémy,1997。开发了一流的多态性的其他方法(请参见Dider Le Botlan的
ST
monad(Recasting MLF,Simon Peyton Jones和John Launchbury,1994)。
关于ocaml - 用OCaml编写此内容的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14707651/