在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
是如何输入的吗?
答案 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
大致具有以下含义:
您可以按如下方式验证:
# (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
参数一样需要四个参数。