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.
Pattern matching comes up in several places in OCaml: as a powerful control structure combining a multi-armed conditional, unification, data destructuring and variable binding; as a shortcut way of defining functions by case analysis; and as a way of handling exceptions.
Every function in OCaml takes exactly one value and returns exactly one result. For instance, our squareRoot function takes one float value and returns one float value. The advantage of always taking one argument and returning one result is that the language is extremely uniform.
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)
.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With