1
votes

Why do i get errors when I write this kind of pattern matching :

type t = A of int | B of float

let f = function
        | (A i | B f) -> true
        | _ -> false

or

let f = function
        | A i | B f -> true
        | _ -> false

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

let f = function
        | (A i | B i) -> true
        | _ -> false

or

let f = function
        | A i | B i -> true
        | _ -> false

Error: This pattern matches values of type ints of type float but a pattern was expected which matches value

2

2 Answers

8
votes

If you provide a single right-hand side for multiple patterns (as you do), OCaml requires that the patterns consistently bind to pattern variables.

In the first situation,

match ... with
  | A i | B f -> ...
  ...

the patterns don't agree on the variables they bind to: the first pattern binds to i, while the second binds to f.

In the second situation,

match ... with
  | A i | B i -> ...
  ...

the patterns don't agree on the type of values to bind to their variables: the first pattern binds a value of type int to i, while the second binds a value of type float to i.

The only way in which these two pattern can consistently bind to variables is not to bind to any variables at all:

match ... with
  | A _ | B _ -> ...
  ...

The complete example then becomes

type t = A of int | B of float

let f = function
  | A _ | B _ -> true
  | _ -> false

(But note that the last arm of the pattern match is superfluous as the first two pattern already exhaustively match all values of your type t. Hence, we get:

let f = function
  | A _ | B _ -> true

This of course is equivalent to writing let f _ = true.)

3
votes

In Or pattern (| pattern), you lose track of which constructors you are in. Therefore, you need to bind the same set of variables to work without referring to constructors.

And OCaml is strongly-typed; a value i cannot have both type int and type float.

If type t has more than two cases, you should write:

let f = function
        | A _ | B _ -> true
        | _ -> false

otherwise:

let f = function
        | A _ | B _ -> true

is enough since pattern matching is already exhaustive.

I agree that Or pattern is quite restrictive, but sometimes it is helpful when you have symmetric cases in your function:

type num = 
    | Int of int
    | Float of float

let add s1 s2 = 
    match s1, s2 with
    | Int i1, Int i2 -> Int (i1 + i2)
    | Int i, Float f | Float f, Int i -> Float (float i +. f)
    | Float f1, Float f2 -> Float (f1 +. f2)