Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type of printfn in F#, static vs dynamic string

Tags:

f#

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.

like image 927
Frank Avatar asked Jan 29 '10 13:01

Frank


People also ask

What is print f called?

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

How do I printf a double?

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.


1 Answers

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.

like image 70
kvb Avatar answered Sep 20 '22 21:09

kvb