我有一个打印字符串并退出的简单函数:
let fatal s =
print_string "Log: ";
print_endline s;
exit 1
我可以使用printf在没有exit 1
的情况下执行类似操作:
let log fmt =
printf ("Log: " ^^ fmt)
此日志函数采用格式字符串并返回一个函数,该函数获取该格式字符串所需的参数并在前面打印“Log:”。 (当然,对于我的实际应用,我的前缀不是那么简单。)
将这两者结合起来并不容易。第一次尝试:
let fatalf fmt =
Printf.printf ("Log: " ^^ fmt)
???
exit 1
问题是我必须返回我的printf
表达式的结果,以便可以将剩余的参数应用于它。一旦我返回了这个值,我就没有流控制来运行exit
。
printf格式化程序%t
看起来很有用,因为它需要一个函数并运行它:
printf ("Log: " ^^ fmt ^^ "%!%t") ... (fun _ -> exit 1)
这似乎不起作用,因为%t
必须是最后的,所以它在写入日志消息后运行,但这意味着退出函数必须在用户指定的参数之后,并且因为没有办法要知道有多少参数可以进行干预,在给出干预参数时,不能生成一个完全应用printf的闭包。
我记得有一些支持命名的printf参数,但这是因为它是错误的。有没有办法模仿,或者实现所谓的“在任意printf之后退出”行为?
答案 0 :(得分:8)
您正在寻找Printf.kprintf
:
let fatalf fmt =
Printf.kprintf (fun str ->
Printf.eprintf "Fatal error: %s !\n%!" str;
exit 1) fmt
kprintf
继续使用string -> 'a
类型,并将其应用于sprintf
的结果,并使用提供的格式。延续的结果是整个调用的结果,正如预期的那样。
答案 1 :(得分:1)
printf
是OCaml的棘手部分,因为它受编译魔术的支持。如果源包含字符串常量且其类型环境需要printf
格式,则编译器会神奇地将字符串常量转换为格式。这很光滑,但是一旦你超越了最简单的用途,你有时需要自己做魔术。特别要注意的是,它必须是一个字符串常量,以便编译器能够强制执行强类型。
您的第二个示例未显示s
参数,但我认为您只是将其关闭。如果您只想使用也作为参数传递的格式打印一个参数,您可以执行以下操作:
let fatalfs f s =
printf ("Log:" ^^ f) s;
exit 1
^^
运算符将两种格式连接成一种格式。
这是一个有这个功能的会话:
$ rlwrap ocaml312
Objective Caml version 3.12.0
# let fatalfs f s = Printf.printf ("Log: " ^^ f) s; exit 1;;
val fatalfs :
('a -> 'b, out_channel, unit, unit, unit, unit) format6 -> 'a -> 'c = <fun>
# fatalfs "Here is the value: [%s]\n" "value";;
Log: Here is the value: [value]
$ echo $?
1
$
请注意,fatalfs实际上是参数s类型的多态。只要格式字符串的类型和第二个参数匹配,它就可以工作。这真是令人印象深刻。