如何在F#中包装printfn?

时间:2019-11-12 14:44:31

标签: f#

我已阅读了有关延续和部分申请的信息;我也知道kprintf函数。

但是我还是不知道该怎么写:

let myPrintFunction format variable_length_arguments_list =
    let a = sprintf format variable_length_ argument_list
    do other things

这是什么语法?

所以我可以像这样使用它:

myPrintFunction "%s : %i" "hello" 3

编辑:

这与How do I implement a method with a variable number of arguments?不同,因为该问题在询问如何创建具有可变数量参数的方法,但是我面临的问题是将可变数量参数传递给下一个函数(sprintf)也需要可变数量的参数。 或者,至少那是我认为问题出在哪里。

基于Scott提出的解决方案的测试代码可以在这里找到:https://dotnetfiddle.net/oCzcS9

3 个答案:

答案 0 :(得分:0)

open System

let myPrintFunction (format: Printf.StringFormat<_>) ([<ParamArray>] args) =
    let a = sprintf format args
    a

myPrintFunction "%s : %i" "hello" 3

答案 1 :(得分:0)

要将PrintF添加为成员函数,这是我能获得的最接近的函数。如您所见,我必须分别传递格式字符串(在构造函数中,否则我可以使用属性设置器)。我找不到像自由函数一样传递格式字符串作为PrintF函数的第一个参数的方法(请参见https://stackoverflow.com/a/58822618/5652483的其他答案)。

此外,如果我取消注释行this.RaiseSomeEvent msg,那么它将中断。因此,我无法找到使PrintF功能产生副作用的方法。

希望其他人可以解决这些问题。

type Foo (format: Printf.StringFormat<_>) =
    member this.RaiseSomeEvent msg = printf "%s" msg

    member this.PrintF ([<ParamArray>] args) =
        let msg = sprintf format args
        //this.RaiseSomeEvent msg
        msg

let foo = Foo("%s : %i")
foo.PrintF "hello" 3

答案 2 :(得分:0)

我想演示ksprintf函数,因为该函数接受一个延续,它将允许您将结果字符串传递给例如日志系统。

出于演示的目的,我们首先创建可以将单个字符串作为输入并将其传递给控制台的东西。

let writeStringToConsole (s: string) = Console.WriteLine ("OUTPUT : " + s)

那么,如果现在只有writeStringToConsole,我们如何使其接受F#格式?

let printToConsole format = Printf.ksprintf writeStringToConsole format

演示其工作原理的示例。

type DU = A | B
let i = 7
let s = "thirteen"
let du = B

printToConsole """an int %i and a string "%s" here""" i s
printToConsole """an int %i and a string "%s" and DU %A here""" i s du

// OUTPUT : an int 7 and a string "thirteen" here
// OUTPUT : an int 7 and a string "thirteen" and DU B here

// Note that OUTPUT is also part of the actual output,
// and it demonstrates how you can add e.g. a timestamp
// or line number or something to the output string, without
// it being part of the formatting.

编辑:一些附加说明

格式字符串必须是文字。这是因为必须在编译时读取文字字符串,以便计算必须返回的函数,以吞噬格式字符串后面的任何值/类型。

例如,如果您执行printToConsole "%i %s %A %A" 7 "x" myType yourType,则将在int -> string -> MyType -> YourType的签名中看到printToConsole

在此系统中,有一种方法可以将纯字符串用作格式字符串,但我不记得它是如何完成的,无论如何都会破坏类型安全性。在进行字符串国际化时,它很方便,并且格式字符串必须来自资源而不是F#源(由于外部翻译服务)。

编辑2:例如日志系统

我创建了一个界面,用于各种日志记录系统,它们几乎共享相同的功能。

type ILogger =

...

    abstract member Debugf: StringFormat<'h, unit> -> 'h
    abstract member Verbosef: StringFormat<'h, unit> -> 'h
    abstract member Infof: StringFormat<'h, unit> -> 'h
    abstract member Warningf: StringFormat<'h, unit> -> 'h
    abstract member Errorf: StringFormat<'h, unit> -> 'h
    abstract member Fatalf: StringFormat<'h, unit> -> 'h

然后,我当前使用的日志记录系统的实现如下所示。

type internal SiLogger(session: Session) =
    let slogf = Printf.ksprintf

...

    interface ILogger with

...

    member _.Debugf format = slogf session.LogDebug format
    member _.Verbosef format = slogf session.LogVerbose format
    member _.Infof format = slogf session.LogMessage format
    member _.Warningf format = slogf session.LogWarning format
    member _.Errorf format = slogf session.LogError format
    member _.Fatalf format = slogf session.LogFatal format

还有一个空的记录器。

let slogf = Printf.ksprintf

let dummyLog _ = () // The parameter is the title string.

let dummy format = slogf dummyLog format

let getNullLogger () =
    { new ILogger with

...

        member _.Debugf format = dummy format
        member _.Verbosef format = dummy format
        member _.Infof format = dummy format
        member _.Warningf format = dummy format
        member _.Errorf format = dummy format
        member _.Fatalf format = dummy format

...

        }