在 OCaml 中,当传递应该适用于Printf.printf. 可能是因为我不完全理解该功能,但我无法确定什么不起作用。
首先,我定义一个函数(用于日志记录):
utop # let log verbosity level str =
  if level <= verbosity then (
    Printf.printf "\nLevel %i: " level;
    Printf.printf str);;
val log : int -> int -> (unit, out_channel, unit) format -> unit = <fun>
一切似乎都很好,但后来我得到了这个:
utop # log 0 0 "%i" 0;;
Error: This function has type
         int -> int -> (unit, out_channel, unit) format -> unit
       It is applied to too many arguments; maybe you forgot a `;'.
尽管以下作品有效:
utop # Printf.printf;;
- : ('a, out_channel, unit) format -> 'a = <fun>
utop # Printf.printf "%i" 0;;
0- : unit = ()
那么,我怎样才能定义一个函数来做我log想要做的事情呢?
编辑:确实,log 0 0 "%i" 0;;看起来参数太多(4 个而不是 3 个),但也是如此Printf.printf "%i" 0;;(2 个而不是 1 个),而且它有效。通过部分应用,这给出了:
utop # Printf.printf "%i";;
- : int -> unit = <fun>
utop # log 0 0 "%i";;
Error: This expression has type (unit, unit) CamlinternalFormatBasics.precision
       but an expression was expected of type
         (unit, int -> 'a) CamlinternalFormatBasics.precision
       Type unit is not compatible with type int -> 'a 
-like函数printf是可变参数的,因为它们接受可变数量的参数。它并不真正特定于printf家族,您可以在 OCaml 中定义自己的可变参数函数,这是类型系统完全支持的。printf 的唯一魔力是编译器将字符串文字转换"foo %d为 类型的值format。
现在,让我们看看函数的类型printf,
('a, out_channel, unit) format -> 'a
请注意,它返回的'a是一个类型变量。因为'a可以是任何东西,所以它也可以是一个函数。是('a, out_channel, unit) format格式字符串的类型,它定义由此格式字符串生成的函数的类型。但重要的是要理解,尽管它"foo %d"看起来像一个字符串,但事实上,它是 type 的一个特殊的内置值_ format,它有一个看起来像字符串的文字(尽管并非所有有效的字符串都是该_ format类型的有效文字) 。
只是为了证明 的第一个参数printf不是字符串,让我们尝试以下操作,
# Printf.printf ("foo " ^ "%d");;
Line 1, characters 14-29:
1 | Printf.printf ("foo " ^ "%d");;
                  ^^^^^^^^^^^^^^^
Error: This expression has type string but an expression was expected of type
         ('a, out_channel, unit) format
现在,当我们知道 printf 不是一个典型的函数时,让我们自己定义一个类似 printf 的函数。为此,我们需要使用kprintf-family 函数,例如,
# #show Printf.ksprintf;;
val ksprintf : (string -> 'd) -> ('a, unit, string, 'd) format4 -> 'a
该函数采用接收我们可以记录的结果字符串的函数,例如,
# let log fmt = Printf.ksprintf (fun s -> print_endline ("log> "^s)) fmt;; 
val log : ('a, unit, string, unit) format4 -> 'a = <fun>
# log "foo";;
log> foo
- : unit = ()
这个结果函数看起来更像是sprintf,即,它将与使用字符串作为输出设备的漂亮打印函数很好地配合(这是一个不同的主题)。您可能会发现使用 或 来定义日志记录函数更容易Printf.kfprintf,使用Format.kasprintf或更好Format.kfprintf。后两个函数有以下类型,
val kasprintf : (string -> 'a) -> ('b, formatter, unit, 'a) format4 -> 'b
val kfprintf : (formatter -> 'a) -> formatter ->
  ('b, formatter, unit, 'a) format4 -> 'b
但是格式的类型与漂亮打印机(通常称为)接受的formatter类型(输出设备的抽象)一起使用。pp因此使用该模块定义的日志函数Format将更好地与现有库配合使用。
因此,使用Format.kasprintf我们可以将您的日志函数定义为,
# let log verbosity level =
  Format.kasprintf (fun msg -> 
      if level <= verbosity then 
        Format.printf "Level %d: %s@\n%!" level msg);;
val log : int -> int -> ('a, Format.formatter, unit, unit) format4 -> 'a = <fun>
这是它的使用方法,
# log 0 0 "Hello, %s, %d times" "world" 3;;
Level 0: Hello, world, 3 times
- : unit = ()