In the following F# code; I would expect that the printfn
is being called three times; each with a string. However, the bottom line does not compile (The type 'string' is not compatible with the type 'Printf.TextWriterFormat<'a>'
).
What is it about the first two lines that means this can work? Aren't they just strings too?
open System
printfn ("\r\n") // Works
printfn ("DANNY") // Works
printfn (DateTime.Now.ToLongTimeString()) // Doesn't compile
The F# compiler statically analyses the format strings you pass to
printfn
to check that the arguments you pass are valid for the format specifiers you use. For example, the following does not compile:since
string
is not compatible with the %d format specifier. The compiler converts valid format strings into aTextWriterFormat<T>
.It can't do this with arbitrary strings, and since it does not do the conversion, you get the type error above.
You can do the conversion yourself however using
Printf.TextWriterFormat
. For example, for a format string requiring astring
and anint
you can use:Since your string has no format placeholders, you can do:
@Lee's answer is correct as per a possible workaround, but it does not describe what happens with your code.
In an expression
printf "foo"
, the"foo"
is not a string to be formatted. Instead, it is input formatter by itself. More specifically, it is a string literal used to infer the actual type of theTextWriterFormat<'T>
.The signature of
printf
is:Since
printfn ("DANNY")
does not contain any format specifiers, the F# compiler infers aTextWriterFormat<unit>
, and the entire expression becomesprintfn ("DANNY") ()
.With a variable, it's impossible to statically predict what format specifiers will be there. Consider if
ToLongTimeString()
method was able to return strings of"%s"
or"%d %d %d"
, what would be the prototype of the returned function?When inferring the correct type, a string literal works fine, but the variable or
let
-binding does not work:In any case, it looks much safer to always use format specifiers:
Update: don't forget to type double colon
;;
after[<Literal>]
value to avoid warning FS0058 in VS2013.