我正在尝试创建一个框架来处理文件和数据.我正在努力解决的一个领域是如何为框架提供日志记录功能,允许框架在不知道正在使用的日志记录的情况下报告消息.
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
Run Code Online (Sandbox Code Playgroud)
理想情况下,我希望此代码可以工作,但我无法确定如何传递记录器功能.
kvb*_*kvb 16
正如Tomas指出的那样,F#中的函数不需要多态参数.在这种情况下,我认为Tomas的方法非常好,因为您可能只需要能够传递string -> 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 }
Run Code Online (Sandbox Code Playgroud)
为了使类型推断更好地工作,您可以使用简单的辅助函数定义一个模块:
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
Run Code Online (Sandbox Code Playgroud)
这个最终结果看起来很像Tomas的解决方案,但是在定义记录器方面给你更多的灵活性,在这种情况下,这可能对你有用,也可能没有用.
Tom*_*cek 13
不幸的是,你不能将像printf参数这样的函数传递给其他函数,然后将它们与多个不同的参数一起使用.问题是这printf是类型的通用函数Printf.TextWriterFormat<'a> -> 'a.替换type参数的实际类型'a是某些函数类型,每次使用时都是不同的printf(例如,'a == string -> unit对于"%s"etc).
在F#中,您不能拥有本身就是通用函数的函数的参数.泛型函数必须是一些全局函数,但您可以通过实际对字符串执行某些操作的函数对其进行参数化.这基本上就是kprintf这样,但你可以更好地命名你的功能:
let logPrintf logger format =
Printf.kprintf logger format
Run Code Online (Sandbox Code Playgroud)
记录器参数化的函数示例如下:
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
Run Code Online (Sandbox Code Playgroud)