包装sprintf不会在编译时而是在运行时抛出错误?

时间:2020-02-22 16:27:49

标签: f#

假设我要使用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函数,但我有点迷失了:-/

1 个答案:

答案 0 :(得分:1)

我假设您想要的是类似sprintf的东西,但是杂乱无章,并且没有类型检查格式。

不幸的是,F#没有'uncurrying'或可变参数函数。 话虽这么说,但也可以使用参数数组。 ParamArray仅对类成员有效,对绑定无效,因此我们可以选择static member,其范围类似于let fn () =

type SpecialPrint =
    static member sprintf (format, [<ParamArray>] args) =
        let index = ref -1        
        let stringFormat = Regex.Replace(format, "%[a-z]", (fun _ -> sprintf "{%d}" (Interlocked.Increment index)))
        String.Format(stringFormat, args)

使用;

let result = SpecialPrint.sprintf ("Hello %s%s", "World", "!") //Hello World!