How to pass a printf-style function to another function in F#

f#, type-inference

Solution

The problem is that when you say `(output : Printf.TextWriterFormat<'a> -> 'a)`, that means "there is some `'a` for which output takes a `Printf.TextWriterFormat<'a>` to an `'a`". Instead, what you want to say is "for all `'a` output can take a `Printf.TextWriterFormat<'a>` and return a `'a`.

This is a bit ugly to express in F#, but the way to do it is with a type with a generic method:

type IPrinter =
    abstract Print : Printf.TextWriterFormat<'a> -> 'a

let OutputStuff (output : IPrinter) =
    output.Print "Header"
    output.Print "Data: %d" 42

OutputStuff { new IPrinter with member this.Print(s) = printfn s }

Problem

I'd like to make a function in F# that accepts a printf-style function as an argument, and uses that argument to output data. Usage would be something like the following: ``` OutputStuff printfn ``` My first attempt was to let the compiler figure it all out for me: ``` let OutputStuff output = output "Header" output "Data: %d" 42 ``` That fails because it decides that `output` is a function taking `string` and returning `unit`, so the second call fails. Next I tried declaring `output` to have the same signature as `printfn`: ``` let OutputStuff (output : Printf.TextWriterFormat<'a> -> 'a) = output "Header" output "Data: %d" 42 ``` This fails because the compiler decides that the real type of `output` is `Printf.TextWriterFormat<string> -> unit`, so again the second call fails. It also generates warning FS0064 indicating that the first call to `output` causes the code to be less generic than the type annotations, which is the crux of the issue here. Last, I tried declaring the output function as a separate type abbreviation: ``` type OutputMe<'a> = Printf.TextWriterFormat<'a> -> 'a let OutputStuff (output : OutputMe<'a>) = output "Header" output "Data: %d" 42 ``` This fails with the same results as the previous attempt. How do I convince the compiler to not specialize the type of `output` and leave it as `Printf.TextWriterFormat<'a> -> 'a`?

Original source

Related problems