BenMan95
BenMan95

Reputation: 195

How to match multiple conditional patterns with one result?

Attempting to compile the following function causes an error:

let balance (left : 'a t) (ele : 'a) (right : 'a t) : 'a t =
  match left,ele,right with
  | N (d',N (_,a,x,b),y,c),z,d when d' - depth d > 1 && ele < y
  | N (d',a,x,N (_,b,y,c)),z,d when d' - depth d > 1 && ele > x
  | a,x,N (d',N (_,b,y,c),z,d) when d' - depth a > 1 && ele < z
  | a,x,N (d',b,y,N (_,c,z,d)) when d' - depth a > 1 && ele > y
      -> new_node (new_node a x b) y (new_node c z d)
  | _ -> new_node left ele right

However, both of the following functions will compile without issue:

let balance (left : 'a t) (ele : 'a) (right : 'a t) : 'a t =
  match left,ele,right with
  | N (d',N (_,a,x,b),y,c),z,d
  | N (d',a,x,N (_,b,y,c)),z,d
  | a,x,N (d',N (_,b,y,c),z,d)
  | a,x,N (d',b,y,N (_,c,z,d))
      -> new_node (new_node a x b) y (new_node c z d)
  | _ -> new_node left ele right
let balance (left : 'a t) (ele : 'a) (right : 'a t) : 'a t =
  match left,ele,right with
  | N (d',N (_,a,x,b),y,c),z,d when d' - depth d > 1 && ele < y
      -> new_node (new_node a x b) y (new_node c z d)
  | _ -> new_node left ele right

How can I get the behavior specified in the first block? Obviously, I could copy the seventh line to each of the preceding patterns, but I'd prefer not to do so.

Upvotes: 0

Views: 1133

Answers (2)

Chris
Chris

Reputation: 36496

As an addendum to what Jeffrey Scofield has already said, beware the following trap.

match 42 with 
| 1 
| n when n mod 2 = 0 -> "foo" 
| n -> "bar"

Or equivalently:

match 42 with 
| (1 | n) when n mod 2 = 0 -> "foo" 
| n -> "bar"

Both get you this error:

Error: Variable n must occur on both sides of this | pattern

A when conditional guard has to work for either pattern. That's why the following will work.

match (3, 2) with 
| (1, n) 
| (3, n) when n mod 2 = 0 -> "foo" 
| n -> "bar"

Equivalently:

match (3, 2) with 
| ((1, n) | (3, n)) when n mod 2 = 0 -> "foo" 
| n -> "bar"

Be prepared for compiler warnings if you bind the same name to different values using patterns joined with |.

Upvotes: 1

Jeffrey Scofield
Jeffrey Scofield

Reputation: 66818

It's true, this is a limitation of OCaml patterns.

When you write this:

match x with
| 1
| 2 -> f x

You're actually writing just one pattern that looks like this:

match x with
| (1 | 2) -> f x

So this (if it were allowed):

match x with
| 1 when a
| 2 when b -> f x

would be equivalent to something like this:

match x with
| (1 when a | 2) when b -> f x

In other words, what you're trying to do is add when clauses into the middle of a pattern. This isn't supported. They're just a feature of match, not of patterns in general.

Upvotes: 3

Related Questions