【问题标题】:How do I create an F# function with a printf style logging argument?如何使用 printf 样式日志记录参数创建 F# 函数?
【发布时间】:2011-04-06 16:37:54
【问题描述】:

我正在尝试创建一个框架来处理文件和数据。我正在努力解决的一个领域是如何向框架提供日志记录功能,允许框架在不了解正在使用的日志记录的情况下报告消息。

let testLogger (source:seq<'a>) logger =
    logger "Testing..."
    let length = source |> Seq.length
    logger "Got a length of %d" length


let logger format = Printf.kprintf (printfn "%A: %s" System.DateTime.Now) format
testLogger [1; 2; 3] logger

理想情况下,我希望这段代码能够工作,但我不知道如何传入记录器函数。

【问题讨论】:

  • 我见过那个,但这对我没有帮助,因为我不想让框架知道 log4net
  • 查看第一个答案,和log4net无关。
  • 我投票结束了这个问题,认为它是重复的 - 但在阅读了您的评论后,我明白它不是(对此感到抱歉)。您想将记录器函数作为参数传递!
  • 完全正确 - 我想将记录器函数作为参数传递!

标签: f# printf


【解决方案1】:

正如 Tomas 所指出的,F# 中的函数不能需要多态参数。在这种情况下,我认为 Tomas 的方法非常好,因为您可能只需要能够传递用于日志记录的 string -&gt; unit 函数。

但是,如果您确实想要传递多态函数的等价物,一种解决方法是使用单个泛型方法创建一个简单类型,并传递该类型的一个实例:

type ILogger = abstract Log : Printf.StringFormat<'a,unit> -> 'a

let testLogger (source:seq<'a>) (logger:ILogger) = 
    logger.Log "Testing..."
    let length = source |> Seq.length        
    logger.Log "Got a length of %d" length

let logger = { 
    new ILogger with member __.Log format = 
        Printf.kprintf (printfn "%A: %s" System.DateTime.Now) format }

为了更好地使用类型推断,您可以使用简单的辅助函数定义一个模块:

module Log =
    let logWith (logger : ILogger) = logger.Log

let testLogger2 (source:seq<'a>) logger =
    Log.logWith logger "Testing..."
    let length = source |> Seq.length        
    Log.logWith logger "Got a length of %d" length

这个最终结果看起来很像 Tomas 的解决方案,但在定义记录器方面给您提供了更多的灵活性,在这种情况下,这可能对您实际上有用,也可能没有。

【讨论】:

  • +1 我非常喜欢您的第一种方法,因为您可以避免使用全局函数。
  • 不错的方法。我已将 Tomas 标记为正确答案,但我确实喜欢这种方法,我可能会使用它
【解决方案2】:

不幸的是,您不能将像printf 这样的函数作为参数传递给其他函数,然后将它们与多个不同的参数一起使用。问题是printfPrintf.TextWriterFormat&lt;'a&gt; -&gt; 'a 类型的泛型函数。替换类型参数'a 的实际类型是某个函数类型,每次使用printf 时都会有所不同(例如'a == string -&gt; unit 用于"%s" 等)。

在 F# 中,您不能拥有本身就是泛型函数的函数的参数。通用函数必须是一些全局函数,但您可以通过实际对字符串执行某些操作的函数对其进行参数化。这基本上是 kprintf 所做的,但您可以更好地命名您的函数:

let logPrintf logger format = 
    Printf.kprintf logger format

记录器参数化的函数示例如下:

let testLogger (source:seq<'a>) logger =
    logPrintf logger "Testing..."
    let length = source |> Seq.length
    logPrintf logger "Got a length of %d" length


let logger = printfn "%A: %s" System.DateTime.Now
testLogger [1; 2; 3] logger

【讨论】:

    猜你喜欢
    • 2011-07-13
    • 1970-01-01
    • 2011-10-25
    • 2020-07-19
    • 1970-01-01
    • 1970-01-01
    • 2011-04-06
    • 2013-03-30
    • 1970-01-01
    相关资源
    最近更新 更多