3

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.

Trenton
  • 11,678
  • 10
  • 56
  • 60
Björn Lindqvist
  • 19,221
  • 20
  • 87
  • 122
  • Possible duplicate: http://stackoverflow.com/questions/2920094/how-can-i-convert-between-f-list-and-f-tuple – Be Brave Be Like Ukraine Jan 25 '13 at 09:12
  • I understand what you're asking but what's the use case for code of this sort? I mean I can see passing a list as a parameter and I can see functions with variadic argument lists but I'm not quite following where you'd want to use code like this. – Onorio Catenacci Jan 25 '13 at 18:22
  • I'm gluing together together two api:s that deal with 3d vectors differently. One likes to represent them as a three item list and the other has three parameters x, y and z on all functions that uses vectors. – Björn Lindqvist Feb 03 '13 at 00:44

3 Answers3

5

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|].

Daniel
  • 47,404
  • 11
  • 101
  • 179
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.

Community
  • 1
  • 1
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • Thanks. I suppose the anser is "can't be done" without reflection? – Björn Lindqvist Jan 25 '13 at 12:13
  • I think the answer by @Daniel is a good alternative (wtihout reflection) if you're happy to use static methods instead of plain functions. – Tomas Petricek Jan 25 '13 at 16:31
  • "*mainly because F# is statically typed language and so supporting similar patters is difficult*" C++ is a statically typed language but can handle this effortlessly. ;-] – ildjarn Jan 25 '13 at 19:37
1

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.

wmeyer
  • 3,426
  • 1
  • 18
  • 26