[Solved] Help with match clause needed

Hello all.

I have a question regarding the match expression.

I have this function in my first programme and it does what I expect:

let rec mandel_point_iter z c max_iter iter=
  if iter = max_iter
  then max_iter
  else begin
    let (z1, divergent) = mandel_point z c in
    if divergent = true
    then iter
    else mandel_point_iter z1 c  max_iter (iter+1)
  end

I tried to replace the function above with this code to try some Ocaml syntax and the linter keeps telling me that the second match case where all the code is is unused. And the proramme also does not run as I want it.

let rec mandel_point_iter z c iter max_iter =
  match iter with
  | max_iter -> max_iter
  | _        -> let (z1, divergent) = mandel_point z c in
                  match divergent with
                  | true -> iter
                  | false -> mandel_point_iter z1 c max_iter (iter+1) 

What I am trying to achive is that if max_iter equals iter the function should return max_iter, in any other case the other code should be executed.

Any help would be appreciated.

1 Like

Pattern matching lets you match against different values. However, when you match against a variable, as in this case with | max_iter -> ..., a fresh variable is created and there is no constraint on the value, so it will match literally anything. You had the quite reasonable expectation that the value of max_iter would be determined by the value supplied to the fourth argument of your function, which has the same name, max_iter. However, pattern matching and variable binding in OCaml doesn’t work this way: you cannot pattern match against variables bound earlier, you can only match against concrete values and value constructors. Any variables appearing in a pattern will be freshly bound to whatever the pattern matches. In this case, the second occurrence of max_iter in the match expression shadows the first occurrence in the function head, meaning, within the scope of the that branch of the match expression, max_iter just has the same value as iter, no matter what iter was.

The upshot is: pattern matching is only useful if you are trying to (1) match against some concrete values or (2) destructure values to access some sub-value. I’ll give an example of each:

(1)

match n with
| 0 |  2 | 4 | 6 | 8 -> "even under 10"
| number -> "odd or greater than 10: " ^ (string_of_int number)

Here, we are matching whatever value is bound to n against a hand full of concrete integer values, and then providing a wildcard match at the end that will catch any other values.

(2)

match opt_int with
| Some n -> n * n
| _ -> 0

In this case, we are matching a value of type int option in order to destructure the Some n and access the integer value. We end the match with an anonymous wild-card match, and just return a default.

Unfortunately, the code you tried to transform just isn’t a great candidate for pattern matching! (This applies also to the use of pattern matching for booleans in the second clause: a simple conditional is more economical in such cases).

You can consult http://ocaml.org/learn/tutorials/data_types_and_matching.html#Pattern-matching-on-datatypes for a relatively concise overview of pattern matching in OCaml.

7 Likes

Thanks for the very clear answer :slight_smile:

1 Like

In addition to shonfeder’s nice answer, here’s another example which kinda’ demonstrates what you’re going for – you’re can (ab)use the match statement as a C switch statement using when clauses. This can be handy in a pinch if you want to do both things together (say check a condition in one arm and pattern match in another):

let f = function
  | 1 -> foo
  | x when is_prime x -> bar
  | _ -> baz (* x is composite *)

(* if-version *)
let f' z =
    if z = 1 then foo z
    else if is_prime z then bar z
    else baz z

Both these are fairly similar, so you can use either one. Here’s a different example. You’ve got a variant now, so you have to pattern match to examine the value inside:

type t = Foo | Bar of int

(* good *)
let g = function
  | Foo -> foo
  | Bar 1 -> bar_1
  | Bar x when is_prime x -> bar_prime
  | _ -> bar_composite

(* bad *)
let g' = function
  | Foo -> foo
  | Bar x -> (
    match x with
      | 1 -> bar_1
      | z -> 
        if is_prime z then bar_prime
        else bar_composite
    )

(* impossible *)
let g'' z = if z = ???
4 Likes

Very glad I could help!

The behavior of variable shadowing can take some time to get used to, but it can be quite nice once you’re accustomed.

Pattern matching is powerful, but has certain distinct limitations. Unification would have accommodated the approach you tried, but that’s a topic for another language :wink:

Hi. Thanks again for the answers. To be honest I understood that my code was not supposed to work, but not quite sure why Ocaml would be designed like this. I had a moment to brood about it.

In a nutshell does the behaviour come down to the fact that I effectively used decomposition in a special case?

Means

match iter with
| max_iter -> ...

decompositions iter into max_iter and that’s why they are always equal?
Of course this is a border case of decomposition. One parameter into one.

Usually you would have something like for example

match a with
|  ( b , c ) -> ...

where you decomposition a (of type tupel) into its elements b and c. And b and c get created new at this point.

If this thought is right the behaviour would make munch sense to me now.

You’re nearly there; the thing is you can’t match with a predefined variables. You can only pattern match with values. max_iter is a variable binding, not a value.

It is the same as saying:

match iter with
| n -> n

basically whatever iter is, it will return that value.

You can achieve what you want with guard clauses, e.g.

match iter with
| n when n = max_iter -> n
| _ -> ...
1 Like

And note that in case you want to do this:

it’s much clearer just to use an if expression (unless you’re going to do some destructuring earlier or later in the match clauses).

I think you’re understanding of pattern matching in terms of decomposition is spot on: pattern matching only allows for a limited kind of decomposition. One of the limitations is that any variable appearing in the match clause will be made fresh (by shadowing earlier variables of the same name, if needed). Another related limitation, is that the same variable cannot appear twice in one match clause. E.g., if you have a logical bent to your mind, you might think it would be handy to be able to differentiate tuples with the same elements from ones with two different elements like so:

match tuple with
| (a, a) -> "Elements are the same"
| _      -> "Elements are different"

But the limitations of pattern matching don’t allow this kind of deeper structural matching. As in the case of variables bound outside of the scope of the matching clause, this is something that would require unification.

2 Likes