4

I have a utility function that combines logging an error message (including string formatting) with raising an exception (either raise-ing an existing exception or calling failwith using the formatted error message, similar to the logAndFailWithf function below (although in reality it is using a logging framework, not writing to a text file):

let logAndFailWithf exOpt =
    let logFile = new StreamWriter("log.txt")
    Printf.ksprintf <| fun message -> 
        try match exOpt with
            | Some ex -> 
                logFile.WriteLine("{0}\r\n{1}", message, ex)
                raise <| Exception(message, ex)
            | None -> 
                logFile.WriteLine(message)
                failwith message
        finally logFile.Close()

val logAndFailWithf : exOpt:#exn option -> (Printf.StringFormat<'b,'c> -> 'b)

This function works fine if called directly, as in this simple example:

let s = "123x"
let x = try Int32.Parse s
        with | ex -> logAndFailWithf (Some ex) "Failed to parse string s: %s" s

System.Exception: Failed to parse string s: "123x" ---> 
    System.FormatException: Input string was not in a correct format.
    at System.Number.StringToNumber(String str, NumberStyles options, 
    NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
    at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)

However, in the real-world scenario, the logging function is being passed into another function, which is using it to log and throw whenever it encounters an exception, similar to the following:

let exmaple param1 param2 (log: exn option -> Printf.StringFormat<_,_> -> _) =
    let x = try Int32.Parse param1
            with | ex -> log (Some ex) "Failed to parse param1: \"%s\"" param1

    let y = try Int64.Parse param2
            with | ex -> log (Some ex) "Failed to parse param2: \"%s\"" param2

    printfn "Successfully Parsed Parameters:  param1 = %d; param2 = %d" x y

In this case, the compiler reports an error on the second let binding (let y = ...) with the message:

error FS0001: This expression was expected to have type
    'int64'    
but here has type
    'int'

This appears to occur on any usage of the log function in an expression that does not have the same type as the first usage of the log function (in the example above, int64 when binding y instead of int when binding x), and I haven't been able to find a way of expressing the type of the log parameter that both matches the signature of the logAndFailWithf function and works when used in expressions of different types within the same function.

How can I pass this implicitly generic function as a parameter to other functions such that it can be called in expressions of different types?

Aaron M. Eshbach
  • 6,380
  • 12
  • 22

0 Answers0