Type of printfn in F#, static vs dynamic string
我刚开始在Mono中玩弄f_,接着出现了我无法完全理解的问题。查找有关
在FSI中,我运行以下程序:
1 2 3 4 5 | >"hello";; val it : string ="hello" > printfn"hello";; hello val it : unit = () |
只是一个普通的字符串并打印出来。好的。现在,我想声明一个变量来包含相同的字符串并打印它:
1 2 3 4 | > 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>' |
号
我从阅读中了解到,
不过,我想知道这里的打字是怎么回事。显然,
好问题。如果您查看
1 2 3 4 5 6 7 8 | 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 |
如果字符串是静态的(如上述示例中所示),那么编译器仍然可以将正确的泛型参数推断为
1 2 3 | let (s:Printf.TextWriterFormat<_>) ="hello" let (s':Printf.TextWriterFormat<_>) ="Here's an integer: %i" let (s'':Printf.TextWriterFormat<_>) ="Float: %f; Bool: %b" |
号
如果字符串是真正动态的(例如,它是从文件中读取的),那么您需要显式地使用类型参数并像前面的示例中那样调用构造函数。
这只和你的问题有点关系,但我认为这是一个很好的技巧。在C中,我经常使用模板字符串与存储为常量的
1 | String.Format(SomeConstant, arg1, arg2, arg3) |
而不是。。。
1 | String.Format("Some {0} really long {1} and distracting template that uglifies my code {2}...", arg1, arg2, arg3) |
。
但由于
1 | let formatFunction = sprintf"Some %s really long %i template %i" |
。
它刚刚创建了一个函数,该函数接受一个字符串和两个整数作为输入,并返回一个字符串。也就是说,
1 | let foo = formatFunction"test" 3 5 |
我使用f的次数越多,发现部分函数应用的用途就越多。很棒的东西。
我认为,当在
起初,我觉得奇怪的是,一个文本字符串值会有一个不同的推断类型,这取决于它被使用的上下文,但当然,我们在处理数字文本时习惯了这一点,数字文本可能表示整数、小数、浮点数等,这取决于它们出现的位置。
如果要在通过printfn使用变量之前声明该变量,可以使用显式类型声明该变量…
1 | let v ="hello" : Printf.TextWriterFormat<unit> in printfn v |
…或者可以使用EDOCX1的构造函数(9)将普通字符串值转换为必要的类型…
1 2 | let s ="foo" ;; let v = new Printf.TextWriterFormat<unit>(s) in printfn v ;; |
。
正如您正确观察到的,printfn函数采用的是"printf.textWriterFormat<'a>",而不是字符串。编译器知道如何在常量字符串和"printf.textWriterFormat<'a>"之间转换,但不知道如何在动态字符串和"printf.textWriterFormat<'a>"之间转换。
这就引出了一个问题,为什么它不能在动态字符串和"printf.textWriterFormat"之间转换。这是因为编译器必须查看字符串的内容,并确定其中包含哪些控制字符(即%s%i等),由此计算出"printf.textWriterFormat<'a>"类型参数的类型(即"a位")。这是一个由printfn函数返回的函数,意味着printfn接受的其他参数现在是强类型的。
要在示例"printfn"%s"中稍微清楚一点,将"%s"转换为"printf.textwriterformat unit>",这意味着"printfn"%s"的类型是string->unit。