Question

In Python, you can write this:

def add(a, b, c):
    return a + b + c

list_of_args = [4, 5, 6]
print(add(*list_of_args))

The asterisk in front of list_of_args expands the iterable so that its elements are the values of the parameters a, b and c.

Can you do something similar in F#? Specifically, I'm looking for a good or idiomatic F# solution and do not want to muck around with reflection and so on.

Was it helpful?

Solution 2

F# does not have anything like this out of the box - mainly because F# is statically typed language and so supporting similar patters is difficult (list may only contain values of one type, while function may have different parameters).

As mentioned in the linked answer, you can emulate similar idea using reflection, which is going to be slow and unsafe, but if you have really good reason for doing this, you might give it a try.

Using the tupleToList function from the previous answer and a few active patterns, you can write:

// Converts any F# tuple to a list of objects using reflection
let tupleToList t = 
    if Microsoft.FSharp.Reflection.FSharpType.IsTuple(t.GetType()) 
        then Some (Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields t |> Array.toList)
        else None

// Active pattern that accepts any object and extracts its members
// if it is a tuple or a sequence of values (e.g. list)
let (|Arguments|_|) (a:obj) = 
  match a, tupleToList a with
  | _, Some t -> Some t
  | :? System.Collections.IEnumerable as l, _ -> 
      l |> Seq.cast |> List.ofSeq |> Some
  | _ -> None

// Treat the argument as an int (this may fail)
let (|Int|_|) (a:obj) = match a with :? int as n -> Some n | _ -> None

// Function that assumes to get three integers
let f (Arguments [Int a;Int b;Int c]) = 
  printfn "%d" (a + b + c)

f (1, 2, 3) // Call with tuple
f [1;2;3]   // Call with a list
f (1, "hi", 3, 141.1)  // This will fail at runtime, but compiler allows it :-(

This is probably not very idiomatic F# and I would try to avoid it, but it might do the trick.

OTHER TIPS

You could do this:

type T =
  static member func([<ParamArray>] args: 'T[]) = printfn "%A" args

T.func(1, 2, 3)

let args = [|1; 2; 3|]
T.func(args)

Both calls print [|1; 2; 3|].

It would be interesting to know your intention here. If you just need a way to treat the arguments of a specific function as a first-class value, you can simply define the function to take a tuple of values as its single argument:

let f (a, b, c) = a + b + c
let args = (1, 2, 3)
let result = f args

For methods, this is actually the "default style". The only drawback: You cannot really use partial application with such functions/methods.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top