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
let (z1, divergent) = mandel_point z c in
if divergent = true
else mandel_point_iter z1 c max_iter (iter+1)
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.
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:
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.
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).
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
(* impossible *)
let g'' z = if z = ???
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.