I'm trying to create a framework to do some processing of files and data. The one area I'm struggling with is how to provide a logging function to the framework, allowing the framework to report messages without having any knowledge of the logging in use.
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
Ideally I want this code to work, but I can't work out how to pass the logger function in.
Strings in Python are usually enclosed within double quotes ( "" ) or single quotes ( '' ). To create f-strings, you only need to add an f or an F before the opening quotes of your string. For example, "This" is a string whereas f"This" is an f-String.
“F-strings provide a way to embed expressions inside string literals, using a minimal syntax. It should be noted that an f-string is really an expression evaluated at run time, not a constant value. In Python source code, an f-string is a literal string, prefixed with f , which contains expressions inside braces.
As Tomas points out, functions in F# can't require polymorphic arguments. In this case, I think that Tomas's approach is quite nice, since you probably only need to be able to pass around a string -> unit
function which is used for logging.
However, if you really do want to pass around the equivalent of a polymorphic function, one workaround is to create a simple type with a single generic method, and pass an instance of that type:
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 }
To make this work more nicely with type inference, you could define a module with a simple helper function:
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
This end result looks a lot like Tomas's solution, but gives you a bit more flexibility in how you define your logger, which may or may not actually be useful to you in this case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With