16

You could pattern-match against multiple arguments of a function by creating a tuple and then destructuring it in a match expression:

let f x y =
  match x, y with
  | pattern1 -> expr1
  | ...

Alternatively, if you don't need a curried function, you could do this by making f take a tuple as the only argument:

let f (x, y) = function
  | pattern1 -> expr1
  | ...

The advantage of the latter method is that you don't have to write the arguments twice every time you define a function. But functions that take a tuple seems to be not as popular than curried ones.

So which of the two is deemed canonical, or preferred, in the OCaml community?

EDIT: Just as pad pointed out below, I mean let f = function blah blah in the second code snippet.

Pteromys
  • 1,441
  • 2
  • 12
  • 29

4 Answers4

17

This solution is canonical:

let f x y =
  match x, y with
  | pattern1 -> expr1
  | ...

The compiler optimizes this special case and does not actually allocate a block for the tuple (x, y).

Martin Jambon
  • 4,629
  • 2
  • 22
  • 28
11

A tuple is not just a syntactic construct, it represents a real data structure. This means that fun (x,y) is (very slightly) less efficient than f x y in the case were x and y aren't already tupled, because a tuple has to be allocated. If this isn't clear, the rough equivalent in Java would be

void foo(X x, Y y) { ... }
void bar(Tuple<X,Y> t) { ... }

/* client code */
X x = new X();
Y y = new Y();

foo(x, y);  // Just uses x and y directly
bar(new Tuple<X,Y>(x, y)); // Has to "new" a Tuple

For this reason, it's generally preferable to avoid using tuples as function arguments unless you have a good reason to do so.

P.S. A similar consideration applies to datatype declarations, where the following are subtly different:

type 'a foo = Foo of 'a * 'a;
type 'a bar = Bar of ('a * 'a);

Foo is a datatype constructor that takes two arguments. Bar is a constructor that takes one argument (a tuple).

Chris Conway
  • 55,321
  • 43
  • 129
  • 155
5

Actually, f = function... is a shortcut of f (x, y) = match (x, y) with... so:

let f = function 
  | pattern1_of_x_y -> expr1
  | ...

is the same as:

let f (x, y) =
  match x, y with
  | pattern1 -> expr1
  | ...

(Notice that there is a mistake in your second formulation; those two versions are not compatible).

As you pointed out, one cannot avoid using match ... with... in a curried function. Personally, I prefer the curried form of a function since it is more flexible, especially with partial application. Moreover, pattern matching is not only applied in function arguments; they are used essentially everywhere in OCaml, which makes match ... with... construct even more important.

Whenever you spot the usage pattern as above, try to replace match ... with... with function. It is just a matter of style, so there is nothing more prefered here.

pad
  • 41,040
  • 7
  • 92
  • 166
4

The canonical way is a curried function and to match on the tuple, i.e. your first snippet.

This is the way the standard library is written (look at the standard library sources, for example many functions in list.ml).

This is also the way that the implementation is optimized for, especially the native code compiler. If you create a tuple or other block and destroy it immediately without passing it to functions that expect a block, the native code compiler often spots that and avoids allocating a block altogether. Even if you end up allocating a block, it's more efficient to make the duration of the block as short as possible, to increases the chances that the block remains in the minor heap and in the processor cache.

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254