18

在以下 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
4

2 回答 2

21

F# 编译器静态分析您传递给的格式字符串,printfn以检查您传递的参数对于您使用的格式说明符是否有效。例如,以下内容无法编译:

printfn "%d" "some value"

因为string与 %d 格式说明符不兼容。编译器将有效的格式字符串转换为TextWriterFormat<T>.

它不能对任意字符串执行此操作,并且由于它不进行转换,因此您会收到上面的类型错误。

您可以自己进行转换,但是使用Printf.TextWriterFormat. 例如,对于需要 astring和 an的格式字符串,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
于 2013-08-31T19:21:24.973 回答
10

根据可能的解决方法,@Lee 的答案是正确的,但它没有描述您的代码会发生什么。

在表达式printf "foo"中,"foo"不是要格式化的字符串。相反,它本身就是输入格式化程序。更具体地说,它是一个字符串文字,用于推断TextWriterFormat<'T>.

的签名printf是:

printf : TextWriterFormat<'T> -> 'T

由于printfn ("DANNY")不包含任何格式说明符,F# 编译器推断 a TextWriterFormat<unit>,整个表达式变为printfn ("DANNY") ().

使用变量,不可能静态地预测将出现什么格式说明符。考虑如果ToLongTimeString()方法能够返回"%s"or的字符串"%d %d %d",返回函数的原型是什么?

推断正确类型时,字符串文字可以正常工作,但变量或let-binding 不起作用:

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。

于 2013-08-31T23:25:42.383 回答