可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
(extracted from ocaml: exposing a printf function in an object's method, so it can be answered independently)
I have the following (simplified) ocaml code, for a logger:
type log_level = | Error | Warn | Info let ord lvl = match lvl with | Error -> 50 | Warn -> 40 | Info -> 30 let current_level = ref (ord Warn) let logf name lvl = let do_log str = if (ord lvl) >= !current_level then print_endline str in Printf.ksprintf do_log
The logf function can be used with a printf format, as in:
logf "func" Warn "testing with string: %s and int: %d" "str" 42;
Is there any way to achieve the typical logging behaviour of only formatting arguments when they're actually needed? i.e something like:
let logf name lvl <args> = if (ord lvl) >= !current_level then Printf.printf <args>
I'm thinking it's because only the compiler knows how many arguments there will be in a format expression, and I guess there's no such thing as varargs in ocaml? So you can't ever define the full body of a printf function, you can only use currying and let the compiler magic figure it out. Is there any way to achieve what I want? Perhaps with mkprintf?
回答1:
The continuation functions such as Printf.kfprintf are provided specifically to allow format wrappers such as this. It looks something like this:
open Printf type log_level = Error | Warn | Info let ord = function Error -> 50 | Warn -> 40 | Info -> 30 let string_of_lvl = function | Error -> "error" | Warn -> "warn" | Info -> "info" let current_level = ref (ord Warn) let printf_with_info name lvl = kfprintf fprintf stdout "[<%s>] <%s>: " name (string_of_lvl lvl) let logf name lvl = if ord lvl >= !current_level then match lvl with | Error | Warn -> printf | Info -> printf_with_info name lvl else ifprintf stdout
回答2:
This seems to get me some of the way:
let logf name lvl = if (ord lvl) >= !current_level then Printf.fprintf stdout else Printf.ifprintf stdout
I'm surprised, as I thought this might mean !current_level would be evaluated too early (at module load time or something). But changing the current_level at runtime does seem to work correctly. I guess currying isn't pure like in haskell (which makes sense, given the impurity above ;)).
But it restricts me to using the fprintf family. I'd prefer to use ksprintf, because I also want a formatter, which (depending on log level) may add information like:
`[<name>] <level>: <message>`
Perhaps I could achieve this by concatenating formats (using ^^), but I don't see how.
回答3:
Whatever you do, calling any Printf.*printf funciton will parse the format string at runtime. This may change in 4.02. Or you can use some syntax extension for conditional printing.