Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manage debug printing in F#

Tags:

f#

I want to add debug printing to my project with a function having a type signature something like:

bool -> Printf.TextWriterFormat<'a> -> 'a

i.e. it should take a bool indicating whether or not we are in verbose mode, and use that to take the decision about whether to print or not.

For example, lets say dprint : bool -> Printf.TextWriterFormat<'a> -> 'a then I would like this behaviour:

> dprint true "Hello I'm %d" 52;;
Hello I'm 52
val it : unit = ()
> dprint false "Hello I'm %d" 52;;
val it : unit = ()

The idea is that a command line flag can be used to avoid control this output. I also want to avoid a runtime cost in the "not verbose" case. It is possible to define a function that works like this using 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

but printing/ignoring a sequence of numbers with List.iter (dprint b "%A") [1..10000] (b \in {true,false}) takes amount 1.5s for both values of b on my machine.

I came up with another method using reflection that builds an appropriately typed function to discard the formatting arguments:

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>)

but here the reflection seems too expensive (even more so than that done inside the standard libraries complicated definition of printf sometimes).

I don't want to litter my code with things like:

if !Options.verbose then
    printfn "Debug important value: %A" bigObject5

or closures:

dprint (fun () -> printfn "Debug important value: %A" bigObject5)

so, are there any other solutions?

like image 557
rneatherway Avatar asked Jul 19 '12 11:07

rneatherway


People also ask

What is debug logging in Papercut?

The log allows our developers to inspect the internal workings of the PaperCut application and pinpoint the cause of problems. Enabling debug logging is only usually required or requested by the PaperCut Support team. The Print Provider is the component in the PaperCut n-tiered architecture that interfaces with the print queues.

How to debug printing problems in Fedora?

How to debug printing problems. If you are experiencing a problem with printing, please take a look at the common bugs page before filing a bug. If the problem you are seeing is not listed there or none of the workarounds seem to help, please consider filing a bug report to help us make Fedora run better on your hardware.

How does a Bugzilla print job work?

Attach all created files to the bugzilla ticket if needed. When a print job is processed it is sent through a chain of filters to convert the file into a format the printer can understand, and then finally sent to a backend, a program which can transport the data to the printer.

How to get debug logs for a specific device?

So for getting debug logs there is a skeleton: where <backend> is the name of backend which supports your device in capitals (f.e. PIXMA, AIRSCAN, GENESYS, HPAIO). You can find which backend supports your device here.


1 Answers

I like your solution using reflection. How about caching it on the type level so that you pay the price of reflection only once per type? For example:

let rec mkKn (ty: System.Type) =
    if Reflection.FSharpType.IsFunction(ty) then
        let _, ran = Reflection.FSharpType.GetFunctionElements(ty)
        // NOTICE: do not delay `mkKn` invocation until runtime
        let f = mkKn ran
        Reflection.FSharpValue.MakeFunction(ty, fun _ -> f)
    else
        box ()

[<Sealed>]
type Format<'T> private () =
    static let instance : 'T =
        unbox (mkKn typeof<'T>)
    static member Instance = instance

let inline dprint verbose args =
    if verbose then
        printfn args
    else
        Format<_>.Instance

A pragmatist would just use the fast C# formatted printing machinery instead of this. I avoid Printf functions in production code because of the overhead they have, as you point out. But then F# printing definitely feels nicer to use.

My #time results for List.iter (dprint false "%A") [1..10000]:

  • Original version : 0.85
  • Original version with reflection : 0.27
  • The proposed version : 0.03
like image 59
t0yv0 Avatar answered Sep 28 '22 09:09

t0yv0