Joe Buschmann

let topics = [csharp; specflow; fun]

F# Functions: Tuple Syntax

I’m relatively new to F# and functional programming and recently worked through an issue that had me perplexed.  The issue was with creating functions with the “tuple syntax” versus the normal syntax of separating arguments by spaces.

Consider the simple functions below that adds numeric arguments together.  They are constructed in two ways.  The first is by using a tuple to group the two arguments together.  It looks very similar to the syntax from other languages such as C#.  In the second declaration, the arguments are separated by spaces.

// The arguments are grouped using a tuple.
let add1(x, y) = x + y

// The arguments are listed separately.
let add2 x y = x + y

I initially thought that the signatures of the two functions were the same:  int -> int –> int; however that isn’t the case.  The signature for the first is:  int * int –> int with a tuple being passed as the single argument.  So the tuple syntax is more than just another way of doing the same thing.  A tuple is actually created, and it’s arguments are passed to the function body.  I discovered this by trying to interchange the two styles when calling the methods.  This left me scratching my head trying to figure out where the syntax problem was.

The snippet below from the F# Interactive Window shows an attempt to call the add1 function without using the tuple syntax. Notice the error message.

> let add1(x, y) = x + y;;

val add1 : int * int -> int

> add1 3 4;;
  add1 3 4;;

stdin(2,1): error FS0003: This value is not a function and cannot be applied

Using the tuple syntax, no error is thrown, and the expected result is returned.  You can even create a tuple value using the let binding and pass it in.

> add1(3, 4);; 
val it : int = 7
> let args = (3, 4);;

val args : int * int = (3, 4)

> add1 args;; 
val it : int = 7

Conversely, when the function is declared without the tuple syntax, its calling sites cannot use the tuple syntax.

> let add2 x y = x + y;;

val add2 : int -> int -> int

> add2(3, 4);;
  add2(3, 4);;

stdin(5,6): error FS0001: This expression was expected to have type
but here has type
    'a * 'b
> add2 3 4;; 

val it : int = 7

Because the tuple syntax resembles how you would create a method in C#, I thought it was simply another way of declaring a function with multiple arguments, but that’s not the case.  It creates a function with a single argument, a tuple, which has multiple values.

Which way is preferred?  Well, that depends on the situation.  If the two arguments are related in some way, then grouping them as a tuple makes sense.  If they are unrelated, then the space delimited syntax would be better so callers could take advantage of partial function application and other functional language features.