I have a generic function using statically resolved type parameters
let inline divide a b = a / b
with signature ^a -> ^a -> ^a
I can create a wrapper function
let log f =
let result = f()
printfn "Result: %A" result
result
If I then create a function like
let loggedDivide a b = log (fun () -> divide a b)
its signature is float -> float -> float
instead of ^a -> ^a -> ^a
, meaning
loggedDivide 2.0 5.0
loggedDivide 2 5 //error
How can this be done?
Note, something like this misses the point of trying to reuse functions
let logValue a = printfn "Result: %A" a
divide 2.0 5.0 |> logValue
divide 2 5 |> logValue
And things doesn't remain generic this way
let logValueAndReturn a =
printfn "Result: %A" a
a
let divideAndLog a b = divide a b |> logValue
divideAndLog 2.0 5.0
divideAndLog 2 5 //error
You have to make your derived function inline as well:
let inline loggedDivide a b = log (fun () -> divide a b)
That will allow constraints to be propagated:
val inline loggedDivide :
a: ^a -> b: ^b -> ^c
when ( ^a or ^b) : (static member ( / ) : ^a * ^b -> ^c)
The reason for this is that SRTP's is an F# compiler feature, which is resolved at compile-time, so functions get specialized by inlining at the call site.
If you want your function to remaing generic, it has to be inline.
Note that the reason you get your function inferred as operating with int
is because that's the default for \
and other math operators. Otherwise you would get an error indicating ambiguity.