我想使用具有类型签名的函数向我的项目添加调试打印,例如:
bool -> Printf.TextWriterFormat<'a> -> 'a
即它应该接受一个布尔值来指示我们是否处于详细模式,并使用它来决定是否打印。
例如,假设dprint : bool -> Printf.TextWriterFormat<'a> -> 'a
我想要这种行为:
> dprint true "Hello I'm %d" 52;;
Hello I'm 52
val it : unit = ()
> dprint false "Hello I'm %d" 52;;
val it : unit = ()
这个想法是可以使用命令行标志来避免控制此输出。我还想避免“不冗长”情况下的运行时成本。可以使用以下方法定义一个像这样工作的函数kprintf
:
let dprint (v: bool) (fmt: Printf.StringFormat<'a,unit>) =
let printVerbose (s: string) =
if v then System.Console.WriteLine(s)
fmt |> Printf.kprintf printVerbose
但是在我的机器上打印/忽略带有List.iter (dprint b "%A") [1..10000]
(b \in {true,false}) 的数字序列对于 b 的两个值都需要 1.5 秒。
我想出了另一种使用反射的方法,它构建了一个适当类型的函数来丢弃格式化参数:
let dprint (v: bool) (fmt: Printf.TextWriterFormat<'a>) : 'a =
let rec mkKn (ty: System.Type) =
if FSharpType.IsFunction(ty) then
let _, ran = FSharpType.GetFunctionElements(ty)
FSharpValue.MakeFunction(ty,(fun _ -> mkKn ran))
else
box ()
if v then
printfn fmt
else
unbox<'a> (mkKn typeof<'a>)
但是这里的反射似乎太昂贵了(甚至比标准库中的复杂定义printf
有时还要昂贵)。
我不想在我的代码中乱扔以下东西:
if !Options.verbose then
printfn "Debug important value: %A" bigObject5
或关闭:
dprint (fun () -> printfn "Debug important value: %A" bigObject5)
那么,还有其他解决方案吗?