Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OCaml Pattern match with non-constants

Is it possible to do pattern matching on variables instead of constant values:

# let x = 2 in
let y = 5 in
match 2 with
| x -> "foo"
| y -> "bar"
| _ -> "baz";;
            let y = 5 in
Warning 26: unused variable y.
  let x = 2 in
Warning 26: unused variable x.
  | y -> "bar"
Warning 11: this match case is unused.
  | _ -> "baz";;
Warning 11: this match case is unused.
- : string = "foo"

Obviously, with this syntax, the x -> "foo" case takes everything. Is there a way to make it be equivalent to:

match 2 with
| 2 -> "foo"
| 5 -> "bar"
| _ -> "baz"

where the values of the match expressions are determined at runtime?

like image 375
Nick Heiner Avatar asked Dec 10 '22 00:12

Nick Heiner


2 Answers

A when guard is a bad idea here in my opinion. Beginners tend to over-use the pattern-matching syntax that they find convenient, and this result in subtle bugs in their application. For one person that will notice that "hey, | y -> .. here is not what I want", ten other persons will make the mistake and don't notice it directly.

You should discourage such error-prone pattern. My personal advice to beginners is to never use when, and to only use pattern-matching to destructure values of an inductive datatype (such as lists, trees, options, etc.). Pattern-matching on integers is almost always a mistake.

I recommend to use if ... then .. else if .. then .. else instead.

if z = x then "foo"
else if z = y then "bar"
else "baz"

When is it legitimate to use when? It is understandable to have one when the rest of the cases do benefit from pattern-matching (to test nested patterns, etc.), or when the test is performed on values resulting from deep destructuration of the matched value. It can often be translated away by merging two branches and using an if..then..else locally.

One exampe where it is inconvenient to remove is the following (testing combined to destructuring):

match foo with
| (Baz x) when pred x -> ...
| _ -> ... 
like image 135
gasche Avatar answered Jan 08 '23 14:01

gasche


You need when guard:

let x = 2 in
let y = 5 in
match 2 with
| z when z = x -> "foo"
| z when z = y -> "bar"
| _ -> "baz";;

The error messages are very instructive. When you use:

let x = 2 in
...
match 2 with
| x -> "foo"
| ...

the new value x shadows the value x in previous let-bound hence the first error message. Moreover, since the new x matches everything, two below patterns of y and _ are obviously redundant.

Note that matching a constant (match 2 with) is not a good idea.

like image 25
pad Avatar answered Jan 08 '23 13:01

pad