输入模块Printf

时间:2013-12-16 09:05:15

标签: types ocaml

Module Printf的文件中,我不理解('a, out_channel, unit) format的机制,认为我在实践中经常使用它。

例如,以下函数在编译时输入正确:

type t =
  { x: int;
    y: int }

let print (chan: out_channel) (co: t) : unit =
  Printf.fprintf chan "(%d, %d)" x y

let try (co0: t) (co1: t) =
  Printf.fprintf Pervasives.stdout "From %a To %a" print co0 print co1

Printf.fprintf的签名是: out_channel -> ('a, out_channel, unit) format -> 'a,但我看不到('a, out_channel, unit) format"From %a To %a" print co0 print co1匹配的内容。

另外,print的签名是out_channel -> t -> unit,为什么代码中可以接受print co0 print co1

总之,有人可以解释Printf.fprintf Pervasives.stdout "From %a To %a" print co0 print co1是如何输入的吗?

2 个答案:

答案 0 :(得分:6)

OCaml在格式字符串周围有一个打字黑客。这很简单,但对于简单且类型安全的printf函数很有用。

通常,字符串文字键入字符串:

# "(%d, %d)";;
- : string = "(%d, %d)"

但如果它们具有“格式化”类型的上下文,则它们不是:

# ( "(%d, %d)" : (_,_,_) format );;
- : (int -> int -> 'a, 'b, 'a) format = <abstr>

OCaml类型检查器将文字视为格式字符串,然后对其使用特殊的类型规则:它在那里找到两个%d,并给出一个类型(int -> int -> 'a, 'b, 'a) format,这意味着它是一个格式字符串,可以采取2个整数并做一些事情。

将字符串文字与Printf函数一起使用会给出相同的“格式”输入上下文,因此“(%d,%d)”的输入不仅仅是字符串,而是这种格式类型:

# Printf.fprintf stdout "(%d, %d)";;
- : int -> int -> unit = <fun>

(t1, t2, t3) format大致具有以下含义:

  • t1是关于格式字符串如何表现为函数:“%d”对于此t1部分中的某些类型t应该具有“int - &gt; t”,因为它采用整数并执行某些操作(主要是打印)。 / LI>
  • t2是频道的类型
  • t3是给出所有格式参数时的最终结果类型。

您可以按如下方式验证:

# (fun x -> Printf.fprintf stdout x, x) "(%d, %d)";;
- : (int -> int -> unit) * (int -> int -> unit, out_channel, unit) format =
(<fun>, <abstr>)

"(%d, %d)"用于获取2个整数参数,用于将字符串发送到类型为out_channel的stdout,最后返回unit

实际上,format类型只是format6的别名,有6个类型参数,而这3个类型参数用于其他格式字符串输入黑客。但是我们不会从这里继续下去。

现在为“%a”。

# ( "%a" : (_, out_channel, unit) format);;
- : ((out_channel -> 'a -> unit) -> 'a -> unit, out_channel, unit) format = <abstr>

这告诉你Printf.fprintf stdout "%a"有两个参数。一个是out_channel -> 'a -> unit类型的函数,另一个是'a

如果您看到此类型,则很容易看到Printf.fprintf stdout "%a" print co0输入良好。请注意,它不是Printf.fprintf stdout "%a" (print co0)(这是我多年前第一次看到"%a"时错误阅读的内容。)

Printf.fprintf stdout "%a" print co0尝试使用打印机co0打印print。这个子打印的频道当然是stdout,它被赋予Printf.fprintf

答案 1 :(得分:0)

我将描述我的直觉,知道事情真正起作用的人可以纠正我或提供更好的答案。 ('a, out_channel, unit) format是为格式字符串提供的类型。格式字符串不会被解析为字符串,即它没有类型string。相反,例如,"From %a To %a"被解析为具有类型((('a -> 'b -> 'c) -> 'b -> ('a -> 'd -> 'c) -> 'd -> 'c), 'a, 'c) format。当您将此与('a, out_channel, unit) format统一时,您会获得(((out_channel -> 'b -> unit) -> 'b -> (out_channel -> 'd -> unit) -> 'd -> unit), out_channel, unit) format。因此Printf.fprintf Pervasives.stdout "From %a To %a"的类型为(out_channel -> 'b -> unit) -> 'b -> (out_channel -> 'd -> unit) -> 'd -> unit,即它与print co0 print co1参数一样需要四个参数。