F# 中 printfn 的类型,静态与动态字符串 [英] Type of printfn in F#, static vs dynamic string
问题描述
我刚开始在 Mono 中玩弄 F#,出现了以下我不太明白的问题.查了printfn
和TextWriterFormat
的资料也没有什么启发,所以想在这里问一下.
I just began toying around with F# in Mono and the following problem arose that I cannot quite understand. Looking up information on printfn
and TextWriterFormat
didn't bring enlightenment either, so I thought I'm going to ask here.
在 FSI 中,我运行以下内容:
In FSI I run the following:
> "hello";;
val it : string = "hello"
> printfn "hello";;
hello
val it : unit = ()
只是一个普通的字符串并打印出来.美好的.现在我想声明一个变量来包含相同的字符串并打印它:
Just a normal string and printing it. Fine. Now I wanted to declare a variable to contain that same string and print it as well:
> let v = "hello" in printfn v ;;
let v = "hello" in printfn v ;;
---------------------------^
...stdin(22,28): error FS0001: The type 'string' is not compatible with the type 'Printf.TextWriterFormat<'a>'
我从阅读中了解到 printfn
需要一个常量字符串.我也明白我可以用像 printfn "%s" v
这样的东西来解决这个问题.
I understood from my reading that printfn
requires a constant string. I also understand that I can get around this problem with something like printfn "%s" v
.
但是,我想了解这里打字的情况.很明显,"hello"
是 string
类型,v
也是.那为什么会出现类型问题呢?printfn
有什么特别的吗?据我了解,编译器已经对第一个字符串的参数执行了类型检查,因此 printfn "%s" 1
失败.. 这当然不能用于动态字符串,但我假设对于静态情况,这只是编译器端的一种方便.
However, I'd like to understand what's going on with the typing here. Clearly, "hello"
is of type string
as well as v
is. Why is there a type problem then? Is printfn
something special? As I understand it the compiler already performs type-checking on the arguments of the first string, such that printfn "%s" 1
fails.. this could of course not work with dynamic strings, but I assumed that to be a mere convenience from the compiler-side for the static case.
推荐答案
好问题.如果看一下printfn
的类型,就是Printf.TextWriterFormat<'a>->'a
,您将看到编译器在编译时自动将字符串强制转换为 TextWriterFormat
对象,从而推断出适当的类型参数 'a
.如果您想将 printfn
与动态字符串一起使用,您可以自己执行该转换:
Good question. If you look at the type of printfn
, which is Printf.TextWriterFormat<'a> -> 'a
, you'll see that the compiler automatically coerces strings into TextWriterFormat
objects at compile time, inferring the appropriate type parameter 'a
. If you want to use printfn
with a dynamic string, you can just perform that conversion yourself:
let s = Printf.TextWriterFormat<unit>("hello")
printfn s
let s' = Printf.TextWriterFormat<int -> unit>("Here's an integer: %i")
printfn s' 10
let s'' = Printf.TextWriterFormat<float -> bool -> unit>("Float: %f; Bool: %b")
printfn s'' 1.0 true
如果字符串是静态已知的(如上例所示),那么您仍然可以让编译器推断 TextWriterFormat
的正确通用参数,而不是调用构造函数:
If the string is statically known (as in the above examples), then you can still have the compiler infer the right generic argument to TextWriterFormat
rather than calling the constructor:
let (s:Printf.TextWriterFormat<_>) = "hello"
let (s':Printf.TextWriterFormat<_>) = "Here's an integer: %i"
let (s'':Printf.TextWriterFormat<_>) = "Float: %f; Bool: %b"
如果字符串确实是动态的(例如,它是从文件中读取的),那么您需要像我在前面的示例中所做的那样显式使用类型参数并调用构造函数.
If the string is truly dynamic (e.g. it's read from a file), then you'll need to explicitly use the type parameters and call the constructor as I did in the previous examples.
这篇关于F# 中 printfn 的类型,静态与动态字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!