在以下F#代码中;我希望printfn
被召唤三次;每个都有一个字符串。但是,底线不会编译(The type 'string' is not compatible with the type 'Printf.TextWriterFormat<'a>'
)。
前两行是什么意思可行的?它们不仅仅是字符串吗?
open System
printfn ("\r\n") // Works
printfn ("DANNY") // Works
printfn (DateTime.Now.ToLongTimeString()) // Doesn't compile
答案 0 :(得分:19)
F#编译器静态分析您传递给printfn
的格式字符串,以检查您传递的参数是否对您使用的格式说明符有效。例如,以下内容无法编译:
printfn "%d" "some value"
因为string
与%d格式说明符不兼容。编译器将有效格式字符串转换为TextWriterFormat<T>
。
使用任意字符串无法执行此操作,并且由于它不执行转换,因此会出现上面的类型错误。
您可以使用Printf.TextWriterFormat
自行进行转换。
例如,对于需要string
和int
的格式字符串,您可以使用:
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
答案 1 :(得分:8)
在表达式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 。