【发布时间】:2020-02-23 02:48:56
【问题描述】:
假设我想拥有sprintf 的别名,我会这样做:
namespace FSharp
module Core =
let specialsprintf x y =
sprintf x y
这将给我带来与 sprintf 相同的编译时优势(与其 C# 表亲 API System.String.Format 相比),例如类型检查、检查传递的参数数量是否正确等。
但是,假设我想禁用这个编译时的细节,并通过调用下面的 String.Format 来编写一个简单版本的 sprintf。这可能吗?我知道这个目标听起来很愚蠢,但我想做这个心理练习来确保我理解 F# 打字在这里是如何工作的。如果我这样做(假设我们只能传递一个参数):
namespace FSharp
module Core =
let specialsprintf x y =
#if NORMAL_FSHARP
sprintf x y
#else
let x = x.Replace("%s", "{0}")
System.String.Format(x,y)
#endif
它甚至没有编译,错误是:
~/FSharpPlayground.fs(17,17): Error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved. (FS0072) (FSharpPlayground)
嗯,为什么?
好的,如果我这样指定类型:
namespace FSharp
module Core =
let specialsprintf
#if NORMAL_FSHARP
x
#else
(x: string)
#endif
y =
#if NORMAL_FSHARP
sprintf x y
#else
let x = x.Replace("%s", "{0}")
System.String.Format(x,y)
#endif
然后我最终在调用者中出现编译错误:
~/FSharpPlaygroundUse.fs(48,48): Error FS0001: This expression was expected to have type 'obj []' but here has type 'string' (FS0001)
我想我现在需要限定 y 的类型,但不知道该怎么做,以防我想扩展它以使用 2 个参数而不是 1 个(我无法做到使其与ParamArray 属性一起使用)。有些东西告诉我,我可能还需要一个 uncurry 函数,但我有点迷茫:-/
【问题讨论】:
-
它失败了,因为 String.Format 中的第二个参数是一个对象数组。请改用
[| y |]。 -
如果你这样做
#if NORMAL_FSHARP x #else (x: string) #endif并传入字符串以外的其他内容,那么x.Replace("%s", "{0}")将失败,所以我不确定我是否理解目标是什么。您是否试图诱使编译器认为某些东西不是类型安全的?
标签: f#