F# の書式指定文字列

printf 使ってるとすぐに気付くんだけど

let s = "hoge"
printfn s

これ、コンパイル通らないですね。

error FS0001: 型 'string' は型 'Printf.TextWriterFormat<'a>' と互換性がありません


Console.WriteLine みたいに単純に文字列を受け取るわけじゃないみたいです。


こういう間違った指定もコンパイルエラーになります。

printfn "%s, %i" 100 200

つまり、コンパイル時に文字列リテラルを解析して各プレースホルダーの型を解決できちゃう、と。
これはなかなか粋なことしてくれます!


ちなみに Printf.TextWriterFormat<'a> は PrintFormat<'a, System.IO.TextWriter, unit, unit> の型省略形です。
次の検証結果を見るとよくわかりますが、文字列リテラルを直接 PrintfFormat 型として扱うことができるってことですね。

> let format:PrintfFormat<_,unit,unit,unit> = "%s %i";;
val format : PrintfFormat<(string -> int -> unit),unit,unit,unit>

PrintfFormat のコンストラクタの引数に対しては解析処理は働きません。これは文字列リテラルを普通に string 型の引数として渡しているだけだから当然です。

> let format = new PrintfFormat<_,unit,unit,unit>("%s %i");;

error FS0030: 値の制限。値 'format' は次のジェネリック型を持つと推論されました。
    val format : PrintfFormat<'_a,unit,unit,unit>
単純なデータ用語として 'format' を定義するか、明示的な引数を使用して関数にするか、ジェネリックにする意図がない場合は型の注釈を追加してください。