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 表示格式字符串作为函数的行为方式:对于此 t1 部分中的某些类型 t,“%d”应该具有“int -> t”,因为它需要一个整数并执行某些操作(主要是打印)。
- 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 个整数参数,用于发送一个字符串到 stdout,其类型为out_channel
,最后它返回unit
.
实际上format
type 只是一个别名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
.