Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Discriminated union pattern matching by function call

My question is inspired by this one: link

Here is a code:

    type A = 
        | X of int * int
        | Y of string

    let f (A.X(a, b)) = a + b 

It works, but with a warning: warning

Makes sense; I have no match for Y.

But if I add a line

    let f (A.Y(s)) = 10

Then I get an error:

error

Is there a nice way to fix it and still use pattern matching in function parameters? If not, then why did they create such strange syntax, which always leads to a warning?

like image 337
Rustam Avatar asked Dec 08 '22 10:12

Rustam


2 Answers

You need to pattern match on the argument:

let f = function
| X(a, b) -> a + b
| Y(_) -> 10

When you define

let f (A.X(a, b)) = a + b 

f has type A -> int, not A.X -> int. It is not defined for values which are instances of A.Y, so you get the incomplete match warning.

Your second definition of f also has type A -> int and so is a duplicate definition of the first, hence the error. If you want to write a total function over some union type you should use pattern matching with function or match.

EDIT: In response to the comment, if you have multiple arguments you want to match at the same time, you can use match e.g.:

let f a1 a2 =
    match (a1, a2) with
    | (X(a, b), X(a', b')) -> a + b
    | (X(a, b), Y(s)) -> a + 10
    | (Y(s), X(a, b)) -> 10
    | (Y(s), Y(s')) -> 20
like image 181
Lee Avatar answered Jan 21 '23 00:01

Lee


Some pattern matches can be complete and might be useful in function parameters, e.g. this definition of fst is pattern matching a tuple and is complete for all 2 tuples.

let fst (a,_) = a

Some other examples:

type Container = Container of string
let unwrap (Container(v)) = v

type Person = { Name:string; Age:int }
let getName {Name=name} = name
like image 33
Leaf Garland Avatar answered Jan 21 '23 01:01

Leaf Garland