简体   繁体   中英

How to define printfn equivalent in F#

Since I do research with F# (in particular, using F# interactive), I'd like to have switchable "print-when-in-debug" function.

I can do

let dprintfn = printfn

F# interactive says

val dprintfn : (Printf.TextWriterFormat<'a> -> 'a)

and I can use

dprintfn "myval1 = %d, other val = %A" a b

whenever I want in my scripts.

Now I'd like to define dprintfn differently, so that it would ignore all its arguments yet being syntax-compatible with printfn . How?


The closest (yet non-working) variant I have in mind is:

let dprintfn (arg: (Printf.TextWriterFormat<'a> -> 'a)) = ()

but it the following doesn't compile then dprintfn "%A" "Hello" , resulting in error FS0003: This value is not a function and cannot be applied .

PS I currently use an alias for Debug.WriteLine(...) as work-around, but the question is still interesting for understading F#'s type system.

You can use the kprintf function, which formats a string using the standard syntax, but then calls a (lambda) function you specify to print the formatted string.

For example, the following prints the string if debug is set and otherwise does nothing:

let myprintf fmt = Printf.kprintf (fun str -> 
  // Output the formatted string if 'debug', otherwise do nothing
  if debug then printfn "%s" str) fmt

I've been profiling my application and found that debug formatting causes significant performance issues . Debug formatting occurs on almost every string of the code, due to the nature of the application.
Obviously, this has been caused by kprintf which unconditionally formats and then passes a string to a predicate.
Finally, I came up with the following solution that may be useful for you:

let myprintf (format: Printf.StringFormat<_>) arg =
    #if DEBUG 
        sprintf format arg
    #else
        String.Empty
    #endif

let myprintfn (format: Printf.TextWriterFormat<_>) arg =
    #if DEBUG
        printfn format arg
    #else
        ()
    #endif

Usage is quite simple, and format checking works fine:

let foo1 = myprintf "foo %d bar" 5
let foo2 = myprintf "foo %f bar" 5.0

// can't accept int
let doesNotCompile1 = myprintf "foo %f bar" 5
// can't accept two arguments
let doesNotCompile2 = myprintf "foo %f bar" 5.0 10

// compiles; result type is int -> string
let bar = myprintf "foo %f %d bar" 5.0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM