In F#, how do you curry ParamArray functions (like

2020-07-09 09:31发布

问题:

In F#, how do you curry a function that accepts a variable number of parameters?

I have code like this...(the log function is just an example, the exact implementation doesn't matter)

let log (msg : string) =
    printfn "%s" msg

log "Sample"

It gets called throughout the code with sprintf formatted strings, ex.

log (sprintf "Test %s took %d seconds" "foo" 2.345)

I want to curry the sprintf functionality in the log function so it looks like...

logger "Test %s took %d seconds" "foo" 2.345

I've tried something like

let logger fmt ([<ParamArray>] args) =
    log (sprintf fmt args)

but I cannot figure out how to pass the ParamArray argument through to the sprintf call.

How is this done in F#?

回答1:

let log (s : string) = ()
let logger fmt = Printf.kprintf log fmt

logger "%d %s" 10 "123"
logger "%d %s %b" 10 "123" true


回答2:

The behaviour of printf-like functions in F# is in some way special. They take a format string, which specifies what the expected arguments are. You can use Printf.kprintf as shown by desco to define your own function that takes a format string, but you cannot change the handling of format strings.

If you want to do something like C# params (where the number of arguments is variable, but does not depend on the format string), then you can use ParamArray attribute directly on a member:

open System

type Foo = 
  static member Bar([<ParamArray>] arr:obj[]) =
    arr |> Seq.mapi (fun i v -> printfn "[%d]: %A" i v)

Then you can call Foo.Bar with any number of arguments without format string:

Foo.Bar("hello", 1, 3.14)

This is less elegant for string formatting, but it might be useful in other situations. Unfortunatelly, it will only work with members (and not with functions defined with let)