I just began toying around with F# in Mono and the following problem arose that I cannot quite understand. Looking up information on printfn
and TextWriterFormat
didn't bring enlightenment either, so I thought I'm going to ask here.
In FSI I run the following:
> "hello";; val it : string = "hello" > printfn "hello";; hello val it : unit = ()
Just a normal string and printing it. Fine. Now I wanted to declare a variable to contain that same string and print it as well:
> let v = "hello" in printfn v ;; let v = "hello" in printfn v ;; ---------------------------^ \...\stdin(22,28): error FS0001: The type 'string' is not compatible with the type 'Printf.TextWriterFormat<'a>'
I understood from my reading that printfn
requires a constant string. I also understand that I can get around this problem with something like printfn "%s" v
.
However, I'd like to understand what's going on with the typing here. Clearly, "hello"
is of type string
as well as v
is. Why is there a type problem then? Is printfn
something special? As I understand it the compiler already performs type-checking on the arguments of the first string, such that printfn "%s" 1
fails.. this could of course not work with dynamic strings, but I assumed that to be a mere convenience from the compiler-side for the static case.
"printf" is the name of one of the main C output functions, and stands for "print formatted". printf format strings are complementary to scanf format strings, which provide formatted input (lexing aka. parsing).
We can print the double value using both %f and %lf format specifier because printf treats both float and double are same. So, we can use both %f and %lf to print a double value.
Good question. If you look at the type of printfn
, which is Printf.TextWriterFormat<'a> -> 'a
, you'll see that the compiler automatically coerces strings into TextWriterFormat
objects at compile time, inferring the appropriate type parameter 'a
. If you want to use printfn
with a dynamic string, you can just perform that conversion yourself:
let s = Printf.TextWriterFormat<unit>("hello") printfn s let s' = Printf.TextWriterFormat<int -> unit>("Here's an integer: %i") printfn s' 10 let s'' = Printf.TextWriterFormat<float -> bool -> unit>("Float: %f; Bool: %b") printfn s'' 1.0 true
If the string is statically known (as in the above examples), then you can still have the compiler infer the right generic argument to TextWriterFormat
rather than calling the constructor:
let (s:Printf.TextWriterFormat<_>) = "hello" let (s':Printf.TextWriterFormat<_>) = "Here's an integer: %i" let (s'':Printf.TextWriterFormat<_>) = "Float: %f; Bool: %b"
If the string is truly dynamic (e.g. it's read from a file), then you'll need to explicitly use the type parameters and call the constructor as I did in the previous examples.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With