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
答案 0 :(得分:8)
正确的方法来移植这个确切的代码片段(我猜,是的 来自this webpage)即将使用 这个FAQ条目中解释的第一类多态性: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
的唯一方法
type是在字符串上调用参数函数,所以实际上你是
只做返回一个字符串,并调用该函数(继续)
在上面。这可以改写为:
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
就属于这种情况,但<{1}}的情况不是 a(多态)参数中的'a
。类型系统完全尊重ML(Hindley-Damas-Milner)推理系统的这种限制,不会推断出这种类型,因此拒绝需要它作为错误类型的程序(意思不是“错误的”,而是“我无法证明它是正确的”) )。
这个限制使得推理系统都是可判定的(完整系统F的类型推断是不可判定的(Joe B. Wells))和“principal”(如果你不知道什么是主要类型,请参见this SO discussion;简而言之,这意味着推理引擎总是选择最通用的类型),但它也拒绝了类型很好的程序,这是安全类型系统常见的祸害。
大多数ML语言(OCaml,Haskell ......)在某些时候遇到了他们真正希望能够编写的代码的这种限制。对于九十年代的OCaml,在使用该语言编写面向对象编程模式时(参见examples in the manual)。
这就是为什么第一类多态首先在方法类型中引入,然后扩展到记录(一级模块是一个独立的,更新近的添加,也允许这样)。有一个很好的理由为什么它或多或少地局限于某些意义上具有“字段”的“事物”,因为这给出了(必要的)多态性注释的自然方式,并且字段投影是一个很好的地方。 type-checker测试实例化多态值的必要性 - 如果你愿意,这会使理论更简单。
对于OCaml社区这一时期的研究工作,请参见例如Extending ML with Semi-Explicit Higher-Order Polymorphism,Jacques Garrigue和DidierRémy,1997年。开发了其他一流多态的方法(参见第5节(相关工作)) Dider Le Botlan的Recasting MLF概述了文献),以及其他用例,特别是Haskell的ST
monad(Lazy Functional State Threads,Simon Peyton Jones和John Launchbury,1994)
答案 1 :(得分:1)
从这一行
| MyInt i -> printerf string_of_int i
编译器推断printerf
的第一个参数应该具有类型int -> string
,但是从下一行开始
| MyFloat x -> printerf string_of_float x
似乎printerf
的第一个参数应该是float -> string
。
我想你知道OCaml对于整数和实数有不同的类型......所以printerf
类型的这种碰撞会让编译器生病。
您希望此代码中printerf
的类型是什么类型的?也许你应该避免高阶函数并实现转换为字符串更直接?