ocaml printf function: skip formatting entirely if some condition holds

匿名 (未验证) 提交于 2019-12-03 02:33:02

问题:

(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.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!