2013-08-31 43 views
15

在下面的F#代碼中;我期望printfn被稱爲三次;每個都有一個字符串。但是,底線不能編譯(The type 'string' is not compatible with the type 'Printf.TextWriterFormat<'a>')。爲什麼F#的printfn使用文字字符串,但不使用字符串類型的值?

是什麼樣的頭兩行,這意味着這可以工作?他們不只是絃樂?

open System 

printfn ("\r\n") // Works 
printfn ("DANNY") // Works 
printfn (DateTime.Now.ToLongTimeString()) // Doesn't compile 

回答

18

F#編譯器靜態分析傳遞給printfn檢查你傳遞參數的有效期爲您使用的格式說明格式字符串。例如,下面的不編譯:

printfn "%d" "some value" 

因爲string不與%d格式說明兼容。編譯器將有效的格式字符串轉換爲TextWriterFormat<T>

它不能與任意的字符串做到這一點,因爲它不會做轉換,你會得到上述類型的錯誤。

你可以做但是自己使用Printf.TextWriterFormat轉換。 例如,對於需要stringint格式字符串,你可以使用:

let f = Printf.TextWriterFormat<string -> int -> unit>("The length of '%s' is: %d") 
printfn f "something" 9 

由於您的字符串沒有格式的佔位符,你可以這樣做:

let f = Printf.TextWriterFormat<unit>(DateTime.Now.ToLongTimeString()) 
printfn f 
+0

啊;我忘了編譯器在這裏做魔術;我在想運行時。非常有意義的是,這隻適用於編譯時可用的字符串! –

+0

是從字符串到TextWriterFormat的隱式轉換 F#專門用於printf的東西,還是可以用自己的類型來做到這一點? –

+0

「非常有意義的是,這隻適用於編譯時可用的字符串!」 - 好吧,不。 'let printsd fmt = Printf.TextWriterFormat int - > unit> fmt |> printfn'工作。如果fmt沒有正確的格式說明符,它會在運行時失敗。 –

7

@李的回答是正確的每個可能的解決方法,但它沒有描述你的代碼會發生什麼。

在表達printf "foo",所述"foo"不被格式化的字符串。相反,它本身是輸入格式器。更具體地說,它是一個字符串文字,用於推斷TextWriterFormat<'T>的實際類型。

printf的簽名是:

printf : TextWriterFormat<'T> -> 'T 

由於printfn ("DANNY")不包含任何格式說明中,F#編譯器推斷一個TextWriterFormat<unit>,並且整個表達式變爲printfn ("DANNY")()

有了變量,這是不可能的靜態預測符將會有什麼樣的格式。試想,如果ToLongTimeString()方法能夠返回"%s""%d %d %d"字符串,這將是返回函數的原型?

當推斷正確的類型,字符串字面工作正常,但是變量或let結合位不起作用:

let foo1 = "foo" 
let bar = printf foo // does not compile 
[<Literal>] let foo2 = "foo";; // see update below 
let bar = printf foo2 // compiles fine 

在任何情況下,它看起來更安全,始終使用指定格式有:

printf "%s" "DANNY" 
printf "%s" (DateTime.Now.ToLongTimeString()) 

更新:不要忘了後[<Literal>]價值型雙冒號;;避免在VS2013中警告FS0058

相關問題