使包装F#SRTP功能通用

Bre*_*rry 2 generics logging f# inline

我有一个使用静态解析类型参数的泛型函数

let inline divide a b = a / b
Run Code Online (Sandbox Code Playgroud)

签名 ^a -> ^a -> ^a

我可以创建一个包装函数

let log f = 
    let result = f()
    printfn "Result: %A" result
    result
Run Code Online (Sandbox Code Playgroud)

如果我然后创建一个像这样的函数

let loggedDivide a b = log (fun () -> divide a b)
Run Code Online (Sandbox Code Playgroud)

它的签名float -> float -> float代替^a -> ^a -> ^a,意思是

loggedDivide 2.0 5.0
loggedDivide 2 5 //error
Run Code Online (Sandbox Code Playgroud)

如何才能做到这一点?

注意,这样的事情忽略了尝试重用函数的重点

let logValue a = printfn "Result: %A" a
divide 2.0 5.0 |> logValue
divide 2 5 |> logValue
Run Code Online (Sandbox Code Playgroud)

事情并不像这样通用

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
Run Code Online (Sandbox Code Playgroud)

Gus*_*Gus 6

你必须使你的派生函数内联:

let inline loggedDivide a b = log (fun () -> divide a b)
Run Code Online (Sandbox Code Playgroud)

这将允许传播约束:

val inline loggedDivide :
  a: ^a -> b: ^b ->  ^c
    when ( ^a or  ^b) : (static member ( / ) :  ^a *  ^b ->  ^c)
Run Code Online (Sandbox Code Playgroud)

这样做的原因是SRTP是一个F#编译器功能,它在编译时解析,因此函数通过在调用站点内联来实现.

如果您希望您的函数重新保存泛型,则它必须是内联的.

请注意,将函数推断为使用函数的原因int是因为这是\其他数学运算符的默认值.否则你会得到一个表明含糊不清的错误.