繁体   English   中英

如何在 F# 中包装 printfn?

[英]How to wrap printfn in F#?

我已阅读有关延续和部分应用程序的信息; 我也知道 kprintf function。

但我仍然不知道如何写这样的东西:

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

this this 的语法是什么?

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

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

编辑:

这与如何实现具有可变数量 arguments 的方法不同? 因为这个问题是在询问如何使用可变数量的 arguments 制作方法,但我面临的问题是将可变数量的参数传递给下一个 function(sprintf),它也采用可变数量的 ZDBC11CAA5BDA27F77E6FB4DABD8 或者,至少这就是我认为问题所在。

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

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

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

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.

编辑:一些附加说明

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

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

有一种方法可以在这个系统中使用纯字符串作为格式字符串,但我不记得它是如何完成的,而且无论如何它破坏了类型安全。 它在进行字符串国际化时派上用场,并且由于外部翻译服务,您的格式字符串必须来自资源而不是 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

还有一个 null 记录器。

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

...

        }
open System

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

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

要将 PrintF 添加为成员 function,这是我能得到的最接近的。 如您所见,我必须单独传递格式字符串(在构造函数中,或者我可以使用属性设置器)。 I could find no way to pass the format string as the first parameter of the PrintF function as I could for a free function (see my other answer at https://stackoverflow.com/a/58822618/5652483 ).

另外,如果我取消注释this.RaiseSomeEvent msg行,它就会中断。 所以我找不到让 PrintF function 产生副作用的方法。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM