ocaml - 用OCaml编写此内容的正确方法是什么?

标签 ocaml

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的的文献综述),并发现了其他用例,特别是Haskell中的ST monad(Recasting MLF,Simon Peyton Jones和John Launchbury,1994)。

关于ocaml - 用OCaml编写此内容的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14707651/

相关文章:

c++ - 如何将 SWIG 与 "using"一起使用

ocaml - 使用 sexplib 序列化 Ocaml 类型

types - 通配符模式覆盖多态变体的子类型约束

ocaml - 从OCaml中的列表中删除元素的标准方法是什么?

ocaml - 了解 ocaml 中的基本闭包示例

haskell - OCaml 参数传递方案中的断点

ocaml - OCaml调用约定:这是准确的摘要吗?

header - 调用 OCaml 编译器来生成 .cmi

ocaml - 用于简单 OCaml 项目的基本 Oasis 或 Opam 文件

recursion - OCaml 中的匿名递归函数