7
votes

I need to detect a commutative pattern in one of my functions. I thought that writing the following will do the work:

let my_fun a b = match a,b with
  (*...*)
  | a,b
  | b,a when is_valid b -> process b  (***)
  (*...*)

This doesn't work and Ocaml complains with this sub-pattern is unused warning for the line marked with (***).

1) Can someone explain to me what this warning try to say and why this doesn't work?

2) How can I actually write this elegantly without using if then else given the fact that I want to now which argument is_valid?

2) Is it possible to get the intended functionality using only pattern matching and without repeating when is_valid b -> process b as it happens bellow?

let my_fun a b = match a,b with
  (*...*)
  | a,b when is_valid b -> process b
  | b,a when is_valid b -> process b 
  (*...*)

Edit:

In my concrete example a and b are pairs. The function is a bit more complicated but the following will illustrate the case:

let f a b = match a,b with
  | (a1,a2),(b1,b2)
  | (b1,b2),(a1,a2) when b1 = b2 -> a1 + a2

Calling f (1,1) (1,2) will yield pattern match failed. I know understand why (thanks to the answers bellow) and I understand how I can make it work if I have different constructors for each element (as in Ashish Agarwal's answer). Can you suggest a way to make it work in my case?

2

2 Answers

7
votes

The matching works by first matching the pattern, and if that succeedes, then by evaluating the condition with the attached environment from that pattern match. Since a,b will always bind, this is the only case used, and the compiler is correctly reporting that b,a is never used. You'll have to repeat that line,

let my_fun a b = match a,b with
  | a,b when is_valid b -> process b
  | b,a when is_valid b -> process b

Your method could work if you didn't perform the match with variables but to some variant, for example,

let my_fun a b = match a,b with
  | a, `Int b
  | `Int b, a when is_valid b -> process b

Edit: Think of the multiple patterns using one guard as a subexpression,

let my_fun a b = match a,b with
  | ((a,b) | (b,a)) when is_valid b -> process b

You'll see this exemplified in the definition for patterns. It's really one pattern, composed of patterns, being matched.

4
votes

For your first question, the thing to realize is you only have one pattern ((a,b) | (b,a)), which happens to be an "or" pattern. Matching proceeds from left to right in an "or" pattern. Since (a,b) will match anything, the second part will never be used.

For your second question, I don't see the problem, but it depends on the types of a and b. Here's an example:

type t = A of int | B of float

let my_fun a b = match a,b with
  | A a, B b
  | B b, A a when b > 0. ->  (float_of_int a) +. b
  | … -> (* other cases *)

It would also work for simpler types:

let my_fun a b = match a,b with
  | 1,b
  | b,1 when b > 0 -> b + 1
  | … -> (* other cases *)

If you still can't get this to work in your case, let us know the types of a and b you are working with.